diff -r 98a2e215ff01 Lib/distutils/command/build.py --- a/Lib/distutils/command/build.py Sun Aug 10 10:44:21 2014 -0700 +++ b/Lib/distutils/command/build.py Sun Aug 10 16:36:00 2014 -0500 @@ -14,45 +14,31 @@ from distutils.ccompiler import show_compilers show_compilers() -class build(Command): - description = "build everything needed to install" +BUILD_OPTS = [ + ('build-base=', 'b', + "base directory for build library"), + ('build-purelib=', None, + "build directory for platform-neutral distributions"), + ('build-platlib=', None, + "build directory for platform-specific distributions"), + ('build-lib=', None, + "build directory for all distribution (defaults to either " + + "build-purelib or build-platlib"), + ('build-scripts=', None, + "build directory for scripts"), + ('build-temp=', 't', + "temporary build directory"), + ('plat-name=', 'p', + "platform name to build for, if supported " + "(default: %s)" % get_platform()) +] - user_options = [ - ('build-base=', 'b', - "base directory for build library"), - ('build-purelib=', None, - "build directory for platform-neutral distributions"), - ('build-platlib=', None, - "build directory for platform-specific distributions"), - ('build-lib=', None, - "build directory for all distribution (defaults to either " + - "build-purelib or build-platlib"), - ('build-scripts=', None, - "build directory for scripts"), - ('build-temp=', 't', - "temporary build directory"), - ('plat-name=', 'p', - "platform name to build for, if supported " - "(default: %s)" % get_platform()), - ('compiler=', 'c', - "specify the compiler type"), - ('debug', 'g', - "compile extensions and libraries with debugging information"), - ('force', 'f', - "forcibly build everything (ignore file timestamps)"), - ('executable=', 'e', - "specify final destination interpreter path (build.py)"), - ] - boolean_options = ['debug', 'force'] +class BuildOpts(): - help_options = [ - ('help-compiler', None, - "list available compilers", show_compilers), - ] - - def initialize_options(self): + def _init_build_opts(self): + """ Initialize common build options between multiple commands.""" self.build_base = 'build' # these are decided only after 'build_base' has its final value # (unless overridden by the user or client) @@ -63,11 +49,9 @@ self.build_scripts = None self.compiler = None self.plat_name = None - self.debug = None - self.force = 0 - self.executable = None - def finalize_options(self): + def _set_build_opts(self): + """ Set common build options for shared state among commands. """ if self.plat_name is None: self.plat_name = get_platform() else: @@ -114,6 +98,40 @@ self.build_scripts = os.path.join(self.build_base, 'scripts-' + sys.version[0:3]) +class build(Command, BuildOpts): + + description = "build everything needed to install" + + user_options = BUILD_OPTS + [ + ('plat-name=', 'p', + "platform name to build for, if supported " + "(default: %s)" % get_platform()), + ('compiler=', 'c', + "specify the compiler type"), + ('debug', 'g', + "compile extensions and libraries with debugging information"), + ('force', 'f', + "forcibly build everything (ignore file timestamps)"), + ('executable=', 'e', + "specify final destination interpreter path (build.py)"), + ] + + boolean_options = ['debug', 'force'] + + help_options = [ + ('help-compiler', None, + "list available compilers", show_compilers), + ] + + def initialize_options(self): + self._init_build_opts() + self.debug = None + self.force = 0 + self.executable = None + + def finalize_options(self): + self._set_build_opts() + if self.executable is None: self.executable = os.path.normpath(sys.executable) diff -r 98a2e215ff01 Lib/distutils/command/install.py --- a/Lib/distutils/command/install.py Sun Aug 10 10:44:21 2014 -0700 +++ b/Lib/distutils/command/install.py Sun Aug 10 16:36:00 2014 -0500 @@ -10,6 +10,7 @@ import sys, os, string from types import * +from distutils.command.build import BUILD_OPTS, BuildOpts from distutils.core import Command from distutils.debug import DEBUG from distutils.sysconfig import get_config_vars @@ -91,11 +92,11 @@ SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') -class install (Command): +class install(Command, BuildOpts): description = "install everything from build directory" - user_options = [ + user_options = BUILD_OPTS + [ # Select installation scheme and set base director(y|ies) ('prefix=', None, "installation prefix"), @@ -161,7 +162,8 @@ def initialize_options (self): - + """ Initialize Options. """ + self._init_build_opts() # High-level options: these select both an installation base # and scheme. self.prefix = None @@ -213,15 +215,6 @@ self.skip_build = 0 self.warn_dir = 1 - # These are only here as a conduit from the 'build' command to the - # 'install_*' commands that do the real work. ('build_base' isn't - # actually used anywhere, but it might be useful in future.) They - # are not user options, because if the user told the install - # command where the build directory is, that wouldn't affect the - # build command. - self.build_base = None - self.build_lib = None - # Not defined yet because we don't know anything about # documentation yet. #self.install_man = None @@ -250,6 +243,9 @@ # their orders from the installation directory options determined # here. + #Set Shared Build Options + self._set_build_opts() + # Check for errors/inconsistencies in the options; first, stuff # that's wrong on any platform. diff -r 98a2e215ff01 Lib/distutils/dist.py --- a/Lib/distutils/dist.py Sun Aug 10 10:44:21 2014 -0700 +++ b/Lib/distutils/dist.py Sun Aug 10 16:36:00 2014 -0500 @@ -843,20 +843,48 @@ "creating '%s' command object" % command) klass = self.get_command_class(command) - cmd_obj = self.command_obj[command] = klass(self) - self.have_run[command] = 0 + cmd_obj = klass(self) - # Set any options that were supplied in config files + # Set any options that were supplied includes config files # or on the command line. (NB. support for error # reporting is lame here: any errors aren't reported # until 'finalize_options()' is called, which means # we won't report the source of the error.) - options = self.command_options.get(command) + options = self._set_shared_command_options(cmd_obj, command) if options: self._set_command_options(cmd_obj, options) + self.command_obj[command] = cmd_obj + self.have_run[command] = 0 + return cmd_obj + def _set_shared_command_options(self, cmd_obj, command): + """ Reuse command_options from supplied command options. """ + option_keys = list(self.command_options.keys()) + + # Try to get command from command_options if command options wer + # supplied + try: + opt_idx = option_keys.index(command) + options = self.command_options[option_keys[opt_idx]] + option_keys.pop(opt_idx) + except ValueError: + options = {} + + # If Command Has user_options, look for shared options in supplied + # commands + if hasattr(cmd_obj, 'user_options'): + for _command in option_keys: + fancy = FancyGetopt(cmd_obj.user_options) + fancy._grok_option_table() + values = fancy.attr_name.values() + for _opt in self.command_options[_command]: + if _opt not in options and _opt in values: + options[_opt] = self.command_options[_command][_opt] + + return options + def _set_command_options(self, command_obj, option_dict=None): """Set the options for 'command_obj' from 'option_dict'. Basically this means copying elements of a dictionary ('option_dict') to diff -r 98a2e215ff01 Lib/distutils/tests/test_install.py --- a/Lib/distutils/tests/test_install.py Sun Aug 10 10:44:21 2014 -0700 +++ b/Lib/distutils/tests/test_install.py Sun Aug 10 16:36:00 2014 -0500 @@ -17,6 +17,7 @@ from distutils.extension import Extension from distutils.tests import support +from unittest import mock def _make_ext_name(modname): @@ -237,6 +238,23 @@ install_module.DEBUG = False self.assertGreater(len(self.logs), old_logs_len) + def test_finalize_build_options(self): + dist = Distribution({'name': 'xx'}) + + cmd = install(dist) + + #Finalize Options Calls BuildOpts._set_build_options + cmd.build_base= 'base' + cmd.finalize_options() + self.assertEqual(cmd.build_purelib, 'base/lib') + + cmd = install(dist) + dist = mock.MagicMock(ext_modules=False, extra_path=None) + cmd.distribution = dist + cmd.build_base= 'base' + cmd.finalize_options() + self.assertEqual(cmd.build_lib, 'base/lib') + def test_suite(): return unittest.makeSuite(InstallTestCase)