#! /usr/bin/python
#
# copyright (c) 2006 Josselin Mouette <joss@debian.org>
# Licensed under the GNU Lesser General Public License, version 2.1
# See COPYING for details

import sys,os,os.path
from optparse import OptionParser
from py_compile import compile

basepath='/var/lib/python-support'

parser = OptionParser(usage="usage: %prog [options] [directory [...]]")

parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
                  help="verbose output", default=False)
parser.add_option("-b", "--bytecompile", action="store_false", dest="inst_mode",
                  help="byte-compilation mode; modules are only compiled, for the default python version [default]")
parser.add_option("-i", "--install", action="store_true", dest="inst_mode",
                  help="installation mode; compiled modules are made available for all python versions",
		  default=False)
parser.add_option("-c", "--clean", action="store_true", dest="clean_mode",
		  help="clean modules instead of compiling them",
		  default=False)
parser.add_option("-a", "--rebuild-all", action="store_true",
                  dest="rebuild_all", default=False,
		  help="rebuild all modules for a new default python version")
parser.add_option("-f", "--force-rebuild-all", action="store_true",
                  dest="rebuild_everything", default=False,
		  help="rebuild all modules, including installed modules for all python versions.")

(options, args) = parser.parse_args()

if not os.path.isdir(basepath):
  os.mkdir(basepath)

py_versions = ['2.1','2.2','2.3','2.4']
py_installed = [ 'python'+ver for ver in py_versions if os.path.exists('/usr/bin/python'+ver) ]

def debug(x):
  if(options.verbose):
    print x

def bytecompile_only(basedir,dir,file):
  if file.endswith('.py'):
    fullpath=os.path.join(basedir,dir,file)
    debug("compile "+fullpath+'c')
    compile(fullpath)

def clean_simple(basedir,dir,file):
  if file.endswith('.py'):
    fullpath=os.path.join(basedir,dir,file+'c')
    if os.path.exists(fullpath):
      debug("remove "+fullpath)
      os.remove(fullpath)

def compile_allversions(py_versions):
  def compile_allversions_func(basedir,dir,file):
    fullpath=os.path.join(basedir,dir,file)
    for py in py_versions:
      destpath=os.path.join(basepath,py,dir,file)
      try:
        os.makedirs(os.path.join(basepath,py,dir))
      except OSError:
        pass
      if file.endswith('.py'):
        destpath+='c'
        debug("compile "+destpath)
        if py == "python"+sys.version[:3]:
          compile(fullpath,destpath)
        else:
        # D'uh, this one stinks, but it's the only way for non-default
        # versions
          command = 'import py_compile; py_compile.compile("%s","%s")'%(fullpath,destpath)
          os.spawnl(os.P_WAIT, '/usr/bin/'+py, py, '-c', command)
      elif not file.endswith('.pyc'):
        debug("link "+destpath)
        if os.path.exists(destpath):
          os.remove(destpath)
        os.symlink(fullpath,destpath)
  return compile_allversions_func

def clean_allversions(basedir,dir,file):
  fullpath=os.path.join(basedir,dir,file)
  for py in py_installed:
    destpath=os.path.join(basepath,py,dir,file)
    if file.endswith('.py'):
      destpath+='c'
    if os.path.exists(destpath):
      debug("remove "+destpath)
      os.remove(destpath)
      try:
        os.removedirs(os.path.join(basepath,py,dir))
      except OSError:
        pass

def process(basedir,func):
  debug("Looking at %s..."%(basedir))
  for dir, dirs, files in os.walk(basedir):
    dir = dir[len(basedir):].lstrip('/')
    for file in files:
      func(basedir, dir, file)

file_b = os.path.join(basepath,"bytecompiled")
try:
  dirs_b = [ l.rstrip('\n') for l in file(file_b) ]
except IOError:
  dirs_b = []

file_i = os.path.join(basepath,"installed")
try:
  dirs_i = [ l.rstrip('\n') for l in file(file_i) ]
except IOError:
  dirs_i = []

if options.rebuild_everything:
  options.rebuild_all = True
  for ver in py_versions:
    pyver = "python"+ver
    dir = os.path.join(basepath,pyver)
    if os.path.isdir(dir):
      os.spawnlp(os.P_WAIT, 'rm', 'rm', '-rf', dir)

for ver in py_versions:
  pyver = "python"+ver
  dir = os.path.join(basepath,pyver)
  if pyver in py_installed and not os.path.isdir(dir):
    debug("Building all modules in %s..."%(dir))
    for basedir in dirs_i:
      process(basedir,compile_allversions([pyver]))
  if pyver not in py_installed and os.path.isdir(dir):
    debug("Removing obsolete directory %s..."%(dir))
    os.spawnlp(os.P_WAIT, 'rm', 'rm', '-rf', dir)

if options.rebuild_all:
  for basedir in dirs_b:
    process(basedir,clean_simple)
    process(basedir,bytecompile_only)

for basedir in args:
  if options.inst_mode:
    if options.clean_mode:
      action = clean_allversions
      if basedir in dirs_i:
        dirs_i.remove(basedir)
    else:
      action = compile_allversions(py_installed)
      if basedir not in dirs_i:
        dirs_i.append(basedir)
  else:
    if options.clean_mode:
      action = clean_simple
      if basedir in dirs_b:
        dirs_b.remove(basedir)
    else:
      action = bytecompile_only
      if basedir not in dirs_b:
        dirs_b.append(basedir)

  process(basedir,action)

f=file(file_b,"w")
for dir in dirs_b:
  f.write(dir+'\n')
f.close()

f=file(file_i,"w")
for dir in dirs_i:
  f.write(dir+'\n')
f.close()
