import os, sys, types from distutils import core from distutils import fancy_getopt from distutils.core import DEBUG from distutils.errors import * from distutils.util import convert_path from distutils.version import StrictVersion import Dist # For .pkg data files import Structures core.USAGE = """\ Usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] %(script)s --help [cmd1 cmd2 ...] %(script)s cmd --help %(script)s --help-commands %(script)s --help-packages """ # the wrap_text function will collapse 2+ newlines into 1+ newlines INSTALL_SUCCESS = """\ %(name)s version %(version)s has been successfully installed! Please note the installation locations: Example repository config files: %(install_sysconf)s Machine-specific variable data space: %(install_localstate)s Local message catalogs: %(install_l10n)s Demos, default XML catalogs, schemas, profiling code, load tests, and various standard files for installation in new 4Suite repositories: %(install_data)s Python library code, including Python C extensions: %(install_lib)s%(sep)s%(package_root)s Test suites: %(install_tests)s Executable scripts (add this to your executable path): %(install_scripts)s Documentation in various formats, if installed: %(install_docs)s """ class PackageManager(Dist.Dist): global_options = Dist.Dist.global_options + [ ('package=', 'p', "limit command(s) to a given package"), ] display_options = [ ('help-packages', None, 'list all available packages'), ] + Dist.Dist.display_options display_option_names = ['help_packages'] + Dist.Dist.display_option_names def __init__(self, attrs): self.package = None self.package_root = None self.package_info = {} Dist.Dist.__init__(self, attrs) return def finalize_options(self): self.distributions = {} if self.package: if not self.package_info.has_key(self.package): raise DistutilsArgError, \ "package '%s' is unknown, use --help-packages " \ "to get a complete listing" % self.package packages = self.resolve_dependencies(self.package) else: packages = self.package_info.keys() self.sub_packages = self.sort_dependencies(packages) # Initialize the list-type data variables before merging if not self.packages: self.packages = [] if not self.py_modules: self.py_modules = [] if not self.libraries: self.libraries = [] if not self.headers: self.headers = [] if not self.ext_modules: self.ext_modules = [] if not self.scripts: self.scripts = [] if not self.data_files: self.data_files = [] # Build a composite of requested package and dependencies for name in self.sub_packages: self.merge_package(self.package_info[name]) self.developer_mode = (os.path.isdir('CVS') or os.path.isdir('RCS') or os.path.exists('CVS.sandboxinfo')) if self.developer_mode: print "developer mode enabled (CVS directory found)" Dist.Dist.finalize_options(self) return def get_package_distribution(self, package): if self.distributions.has_key(package): return self.distributions[package] attrs = {'script_name' : self.script_name, 'script_args' : [], } metadata = vars(self.metadata) attrs.update(metadata) pkginfo = self.package_info[package] attrs.update(pkginfo) dist = self.distributions[package] = Dist.Dist(attrs) return dist def handle_display_options(self, option_order): from distutils.core import gen_usage if self.help_packages: self.print_packages() print print gen_usage(self.script_name) return 1 return Dist.Dist.handle_display_options(self, option_order) def print_packages(self): packages = self.package_info.keys() packages.sort() max_length = 0 for pkg in packages: if len(pkg) > max_length: max_length = len(pkg) print "Available packages:" for pkg in packages: try: description = self.package_info[pkg]['description'] except KeyError: description = '(no description available)' print " %-*s %s" % (max_length, pkg, description) return def run_commands(self): if DEBUG: print "PackageManager.run_commands():" Dist.Dist.run_commands(self) if self.have_run.get('install') and not self.developer_mode: install = self.get_command_obj('install') subst = {'sep': os.sep} subst.update(vars(self)) subst.update(vars(self.metadata)) subst.update(vars(install)) message = INSTALL_SUCCESS % subst print '-'*78 print wrap_text(message, 78) return def merge_package(self, package_info, overwrite=0): # Any attribute that is not already defined is invalid! metadict = self.metadata.__dict__ dict = self.__dict__ for key, val in package_info.items(): if metadict.has_key(key): if overwrite: metadict[key] = val elif dict.has_key(key): if type(val) is types.TupleType: val = list(val) if overwrite or dict[key] is None: dict[key] = val elif type(dict[key]) is type(val) is types.ListType: # Merge two lists dict[key].extend(val) else: raise DistutilsSetupError, \ "incompatiable types for option '%s'" % key else: raise DistutilsSetupError, \ "invalid distribution option '%s'" % key return def sort_dependencies(self, packages): if DEBUG: print "PackageManager.sort_dependencies():" # Make sure that the dependencies exist in package_info for pkg in packages: try: pkginfo = self.package_info[pkg] for dep in pkginfo.get('dependencies', []): depinfo = self.package_info[dep] except KeyError, key: raise DistutilsFileError('missing dependency: %s' % key) # Remove any duplicates set = {} for name in packages: set[name] = 1 packages = set.keys() # Now we can sort 'em sorted = [] while packages: changed = 0 if DEBUG: print " begin sort:" for pkg in packages[:]: if DEBUG: print " trying", pkg pkginfo = self.package_info[pkg] dependencies = pkginfo.get('dependencies', []) for dep in dependencies: if dep not in sorted: break else: if DEBUG: print " sorted", pkg sorted.append(pkg) packages.remove(pkg) changed = 1 if not changed: raise DistutilsFileError("circular dependency: %s" % packages) return sorted def resolve_dependencies(self, package): if DEBUG: print "PackageManager.resolve_dependencies():" packages = [package] pkg_info = self.package_info[package] for name in pkg_info.get('dependencies', []): if self.package_info.has_key(name): packages.extend(self.resolve_dependencies(name)) return packages def wrap_text (text, width): if text is None: return '' if len(text) <= width: return text chunks = text.split('\n\n') result = [] for chunk in chunks: if chunk.startswith('\n'): # An odd number of newlines, the empty string will become # a newline when this list is joined. result.append('') chunk = chunk[1:] lines = fancy_getopt.wrap_text(chunk, width) result.append('\n'.join(lines)) return '\n'.join(result)