home *** CD-ROM | disk | FTP | other *** search
- #! /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, PyCompileError
-
- basepath='/var/lib/python-support'
- sourcepath='/usr/share/python-support'
- extensionpath='/usr/lib/python-support'
-
- parser = OptionParser(usage="usage: %prog [-v] [-c] package_directory [...]\n"+
- " %prog [-v] [-c] package.dirs [...]\n"+
- " %prog [-v] [-a|-f]")
-
- parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
- help="verbose output", 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 private 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 public modules for all python versions.")
- parser.add_option("-b", "--bytecompile", action="store_true", dest="force_private",
- help="[deprecated] byte-compilation mode: only handle private modules",
- default=False)
- parser.add_option("-i", "--install", action="store_true", dest="force_public",
- help="[deprecated] installation mode: only handle public modules",
- default=False)
-
- (options, args) = parser.parse_args()
-
- if not os.path.isdir(basepath):
- os.mkdir(basepath)
-
- sys.path.append("/usr/lib/python-support/private/")
- import pysupport
- from pysupport import py_supported,py_installed,py_oldversions
-
- def debug(x):
- if(options.verbose):
- print x
-
- # I should use the sets type instead
- def isect(l1,l2):
- return [i for i in l1 if i in l2]
-
- def concat(l1,l2):
- return l1 + [i for i in l2 if i not in l1]
-
- versions_dict={}
-
- def dir_versions(dir):
- if dir not in versions_dict:
- verfile=os.path.join(dir,'.version')
- if dir.startswith(extensionpath):
- # Directory in /usr/lib: only one version
- vers=os.path.split(dir)[1]
- if vers in py_supported:
- versions_dict[dir]=[vers]
- else:
- versions_dict[dir]=[]
- elif dir.startswith(sourcepath):
- # Directory in /usr/share
- extdir=dir.replace(sourcepath,extensionpath,1)
- if os.path.exists(verfile):
- # If we have a .version, use it
- versions_dict[dir]=pysupport.version_list(file(verfile).readline())
- elif os.path.isdir(extdir):
- # Try to obtain the list of supported versions
- # from the extensions in /usr/lib
- versions_dict[dir]=isect(py_supported,os.listdir(extdir))
- else:
- # Otherwise, support all versions
- versions_dict[dir]=py_supported
- else:
- raise "[Internal error] %s: unsupported path for byte-compilation."
- return versions_dict[dir]
-
- def bytecompile_only(basedir,dir,file):
- if file.endswith('.py'):
- fullpath=os.path.join(basedir,dir,file)
- debug("compile "+fullpath+'c')
- try:
- # Note that compile doesn't raise PyCompileError by default
- compile(fullpath, doraise=True)
- except IOError, (errno, strerror):
- sys.stderr.write("WARNING: I/O error while trying to byte-compile %s (%s): %s\n" % (fullpath, errno, strerror))
- except PyCompileError, inst:
- sys.stderr.write("WARNING: compile error while trying to byte-compile %s: %s\n" % (fullpath, inst.msg))
- except:
- sys.stderr.write("WARNING: unexpected error while trying to byte-compile %s: %s\n" % (fullpath, sys.exc_info()[0]))
-
- def clean_simple(basedir,dir,file):
- if file.endswith('.py'):
- for ext in ['c','o']:
- fullpath=os.path.join(basedir,dir,file+ext)
- if os.path.exists(fullpath):
- debug("remove "+fullpath)
- os.remove(fullpath)
-
- def install_modules(versions):
- def install_modules_func(basedir,dir,file):
- if file == '.version':
- return
- fullpath=os.path.join(basedir,dir,file)
- for py in isect(dir_versions(basedir),versions):
- destpath=os.path.join(basepath,py,dir,file)
- try:
- os.makedirs(os.path.join(basepath,py,dir))
- except OSError:
- pass
- if file[-4:] not in ['.pyc','.pyo']:
- debug("link "+destpath)
- # os.path.exists returns False for broken symbolic links
- if os.path.exists(destpath) or os.path.islink(destpath):
- # Oops, the file already exists.
- # Check whether we are conflicting with something else.
- for otherdir in dirs_i:
- if otherdir == basedir:
- continue
- if os.path.exists(os.path.join(otherdir,dir,file)):
- raise "Trying to overwrite %s which is already provided by %s"%(os.path.join(dir,file),otherdir)
- # This is probably a case of postinst re-running.
- # Let's proceed.
- debug("overwrite! "+destpath)
- os.remove(destpath)
- os.symlink(fullpath,destpath)
- # Files are NOT byte-compiled here, this MUST be done later.
- return install_modules_func
-
- def clean_modules(basedir,dir,file):
- fullpath=os.path.join(basedir,dir,file)
- for py in dir_versions(basedir):
- destpath=os.path.join(basepath,py,dir,file)
- l=[destpath]
- if file.endswith('.py'):
- l+=[destpath+'c',destpath+'o']
- for path in l:
- if os.path.exists(path):
- debug("remove "+path)
- os.remove(path)
- try:
- os.removedirs(os.path.join(basepath,py,dir))
- except OSError:
- pass
-
- def clean_modules_gen(versions):
- return clean_modules
-
- 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)
- for file in dirs:
- if os.path.islink(os.path.join(basedir,dir,file)):
- func(basedir, dir, file)
-
- def process_extensions(basedir,func,version=None):
- basedir=basedir.replace(sourcepath,extensionpath,1)
- if os.path.isdir(basedir):
- for vers in os.listdir(basedir):
- if version and vers != version:
- continue
- verdir=os.path.join(basedir,vers)
- if os.path.isdir(verdir):
- process(verdir,func([vers]))
-
- def dirlist_file(f):
- return [ l.rstrip('\n') for l in file(f) if len(l)>1 ]
-
- def generate_pathfile(py):
- path=os.path.join(basepath,py)
- if not os.path.isdir(path):
- return
- pathfile=os.path.join(path,".path")
- debug("Generation of %s..."%pathfile)
- pathlist=[path]
- for f in os.listdir(path):
- f=os.path.join(path,f)
- if f.endswith(".pth") and os.path.isfile(f):
- for l in file(f):
- l=l.rstrip('\n')
- pathlist.append(l)
- pathlist.append(os.path.join(path,l))
- fd=file(pathfile,"w")
- fd.writelines([l+'\n' for l in pathlist])
-
- def bytecompile_all(py,path=None):
- if not path:
- path=os.path.join(basepath,py)
- generate_pathfile(py)
- if not os.path.isdir(path):
- return
- debug("Byte-compilation of whole %s..."%path)
- os.spawnl(os.P_WAIT, '/usr/bin/'+py, py,
- os.path.join('/usr/lib/',py,'compileall.py'), '-q', path)
-
- def bytecompile_privatedir(basedir):
- versionfile=os.path.join(basedir,".pyversion")
- if os.path.isfile(versionfile):
- specific_version=file(versionfile).readline().rstrip('\n')
- bytecompile_all("python"+specific_version,basedir)
- else:
- process(basedir,bytecompile_only)
-
- # Parse arguments
- do_dirs_i=[]
- do_dirs_b=[]
- for arg in args:
- if os.path.isabs(arg):
- if not arg.startswith(sourcepath):
- parser.error("%s is not in the python-support directory."%arg)
- else:
- arg=os.path.join(sourcepath,arg)
- if not os.path.exists(arg):
- parser.error("%s does not exist"%arg)
- if arg.endswith('.dirs'):
- do_dirs_b+=dirlist_file(arg)
- if options.force_public:
- parser.error("Option -i cannot be used with a private module .dirs file.")
- elif os.path.isdir(arg):
- do_dirs_i.append(arg)
- if options.force_private:
- parser.error("Option -b cannot be used with a public module directory.")
- else:
- parser.error("%s is not a directory"%arg)
-
-
- # Read full list from the source directory
- # directories are stuff to be installed
- # foo.dirs files list directories to bytecompile in place
- dirs_b = []
- dirs_i = []
- for f in os.listdir(sourcepath):
- f=os.path.join(sourcepath,f)
- if os.path.isdir(f):
- dirs_i.append(f)
- elif f.endswith('.dirs'):
- dirs_b+=dirlist_file(f)
-
- if options.rebuild_everything:
- options.rebuild_all = True
- for pyver in py_supported:
- dir = os.path.join(basepath,pyver)
- if os.path.isdir(dir):
- os.spawnlp(os.P_WAIT, 'rm', 'rm', '-rf', dir)
-
- # Check for changes in installed python versions
- for pyver in py_oldversions+py_supported:
- dir = os.path.join(basepath,pyver)
- # Check for ".path" because sometimes the directory already exists
- # while the python version isn't installed, because of some .so's.
- if pyver in py_installed and not os.path.isfile(os.path.join(dir,".path")):
- debug("Building all modules in %s..."%(dir))
- for basedir in dirs_i:
- process(basedir,install_modules([pyver]))
- process_extensions(basedir,install_modules,pyver)
- # Byte-compile after running install_modules
- bytecompile_all(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)
- bytecompile_privatedir(basedir)
-
-
- # Now for the processing of what was handed on the command line
- for basedir in do_dirs_b:
- if not options.clean_mode:
- bytecompile_privatedir(basedir)
- else:
- process(basedir,clean_simple)
-
- to_bytecompile=to_clean=[]
- for basedir in do_dirs_i:
- if not options.clean_mode:
- process(basedir,install_modules(py_installed))
- process_extensions(basedir,install_modules)
- to_bytecompile = concat(to_bytecompile,isect(dir_versions(basedir),py_installed))
- else:
- process(basedir,clean_modules)
- process_extensions(basedir,clean_modules_gen)
- to_clean = concat(to_clean,isect(dir_versions(basedir),py_installed))
- # Byte-compile after running install_modules
- for py in to_bytecompile:
- bytecompile_all(py)
- # When removing a module, we have removed the .pyc but we still need
- # to regenerate the .path file
- for py in to_clean:
- generate_pathfile(py)
-