import sys, os from types import ClassType, ListType from distutils import command from distutils.cmd import Command from distutils.core import Distribution, gen_usage, DEBUG from distutils.errors import * from distutils.fancy_getopt import wrap_text # Our new Distribution class class Dist(Distribution): # A mapping of all available commands command_mapping = {} # Add builtin distutils' commands for cmd in command.__all__: command_mapping[cmd] = None command_mapping.update({ 'config' : 'Config', 'build' : 'Build', 'build_py' : 'BuildPy', 'build_ext' : 'BuildExt', #'build_clib' : '', 'build_scripts' : 'BuildScripts', 'build_tests' : 'BuildTests', 'build_docs' : 'BuildDocs', 'build_l10n' : 'BuildL10n', 'build_idl' : 'BuildIdl', #'clean' : '', 'install' : 'Install', 'install_lib' : 'InstallLib', #'install_headers' : '', 'install_scripts' : 'InstallScripts', 'install_data' : 'InstallData', 'install_sysconf' : 'InstallSysconf', 'install_localstate' : 'InstallLocalState', 'install_tests' : 'InstallTests', 'install_docs' : 'InstallDocs', 'install_l10n' : 'InstallL10n', 'sdist' : 'SDist', 'bdist' : 'BDist', #'bdist_dumb' : '', 'bdist_rpm' : 'BDistRpm', 'bdist_inno' : 'BDistInno', 'bdist_wininst' : 'BDistWininst', 'bdist_install' : 'BDistInstall', 'generate' : 'Generate', 'generate_bgen' : 'GenerateBisonGen', 'generate_l10n' : 'GenerateL10n', }) ordered_commands = ['config', 'build', 'clean', 'install', 'sdist', 'bdist', 'generate', ] display_options = [ ('help-compilers', None, 'list available compilers'), ] + Distribution.display_options display_option_names = [ 'help_compilers', ] + Distribution.display_option_names def __init__(self, attrs): # Add our placeholders for arguments from setup() self.dependencies = [] self.l10n = [] self.doc_files = [] self.bgen_files = [] self.tests = [] self.idl_files = [] self.sysconf_files = [] self.localstate_files = [] # The module where configuration variables are written. self.config_module = None self.manifest_templates = [] self.validate_templates = [] Distribution.__init__(self, attrs) if sys.version < '2.3': self.verbose = -1 return def finalize_options(self): Distribution.finalize_options(self) # Python 2.1 uses licence, whereas 2.2 uses license if self.license: self.metadata.license = self.license self.metadata.licence = self.license return # -- Command-line parsing methods ---------------------------------- def parse_command_line(self): rv = Distribution.parse_command_line(self) if self.verbose == -1: # neither --quiet or --verbose specified self.verbose = 0 self._dist_verbose = self.developer_mode else: self._dist_verbose = self.verbose return rv def print_commands(self): max_length = 0 for cmd in self.ordered_commands: if len(cmd) > max_length: max_length = len(cmd) self.print_command_list(self.ordered_commands, "Available commands", max_length) return def print_option_list(self, options, header, max_length): # Generate lines of help text. line_width = 78 opt_width = max_length + 2 + 2 + 2 # room for indent + dashes + gutter text_width = line_width - opt_width big_indent = ' ' * opt_width print header for option in options: long, short, help = option[:3] if long[-1] == '=': long = long[0:-1] # Case 1: no short option at all if short is None: opt_names = long # Case 2: we have a short option, so we have to include it # just after the long option else: opt_names = "%s (-%s)" % (long, short) text = wrap_text(help, text_width) if text: print " --%-*s %s" % (max_length, opt_names, text[0]) for line in text[1:]: print big_indent + line else: print " --%-*s" % (max_length, opt_names) return def _show_help (self, parser, global_options=1, display_options=1, commands=[]): # Determine maximum length of option names options = self.global_options + self.display_options for command in self.commands: klass = self.get_command_class(command) options = options + klass.user_options if hasattr(klass, 'help_options'): options = options + klass.help_options max_length = 0 for option in options: long = option[0] short = option[1] l = len(long) if long[-1] == '=': l = l - 1 if short is not None: l = l + 5 # " (-x)" where short == 'x' if l > max_length: max_length = l # Now print the option tables if global_options: self.print_option_list(self.global_options, "Global options:", max_length) print if display_options: self.print_option_list(self.display_options, "Display options (ignores commands):", max_length) print for command in self.commands: klass = self.get_command_class(command) command_name = getattr(klass, 'command_name', klass.__name__) header = "Options for %r command:" % command_name if hasattr(klass, 'help_options'): self.print_option_list(klass.user_options + klass.help_options, header, max_length) else: self.print_option_list(klass.user_options, header, max_length) print print gen_usage(self.script_name) return def handle_display_options(self, option_order): if self.help_compilers: from distutils.ccompiler import show_compilers show_compilers() return 1 return Distribution.handle_display_options(self, option_order) def get_command_list(self): commands = [] for cmd in self.ordered_commands: commands.append(group) klass = self.get_command_class(group) for sub_command in klass.sub_commands: commands.append(sub_command[0]) rv = [] for cmd in commands: klass = self.cmdclass.get(cmd) if not klass: klass = self.get_command_class(cmd) try: description = klass.description except AttributeError: description = "(no description available)" rv.append((cmd, description)) return rv # -- Command class/object methods ---------------------------------- def get_command_class (self, command): # Try user defined classes first (and already loaded classes) klass = self.cmdclass.get(command) if klass: return klass if not self.command_mapping.has_key(command): raise DistutilsModuleError("invalid command '%s'" % command) base_name = self.command_mapping[command] if not base_name: # Fallback to builtin commands for distutils return Distribution.get_command_class(self, command) module_name = 'Ft.Lib.DistExt.' + base_name klass_name = base_name try: __import__ (module_name) module = sys.modules[module_name] except ImportError: if DEBUG: raise raise DistutilsModuleError, \ "invalid command '%s' (no module named '%s')" % \ (command, module_name) try: klass = getattr(module, klass_name) except AttributeError: if DEBUG: raise raise DistutilsModuleError, \ "invalid command '%s' (no class '%s' in module '%s')" \ % (command, klass_name, module_name) self.cmdclass[command] = klass return klass # -- Methods that operate on the Distribution ---------------------- def announce(self, message, level=1): if sys.version < '2.3': if self._dist_verbose: print message else: Distribution.announce(self, message, level) return # -- Methods that operate on its Commands -------------------------- def run_command (self, command): """Do whatever it takes to run a command (including nothing at all, if the command has already been run). Specifically: if we have already created and run the command named by 'command', return silently without doing anything. If the command named by 'command' doesn't even have a command object yet, create one. Then invoke 'run()' on that command object (or an existing one). """ # Already been here, done that? then return silently. if self.have_run.get(command): return self.announce("running %s" % command) cmd_obj = self.get_command_obj(command) cmd_obj.ensure_finalized() cmd_obj.run() self.have_run[command] = 1 return # -- Distribution query methods ------------------------------------ def has_idl(self): # Used for both build and install return self.idl_files and len(self.idl_files) > 0 def has_l10n(self): # Used for both build and generate return len(self.l10n) > 0 def has_sysconf(self): # Used for install return len(self.sysconf_files) > 0 def has_localstate(self): # Used for install return len(self.localstate_files) > 0 def has_docs(self): # Used for both build and install docs = self.doc_files if not docs: # Our scripts have a built-in documentation generator docs = self.scripts return docs and len(docs) > 0 def has_tests(self): # Used for both build and test return self.tests and len(self.tests) > 0 def has_bgen(self): # Used for both sdist and generate return self.bgen_files and len(self.bgen_files) > 0 def has_scripts(self): # Used for sdist return self.scripts and len(self.scripts) > 0