python (3.11.7)

(root)/
lib/
python3.11/
distutils/
command/
install.py
       1  """distutils.command.install
       2  
       3  Implements the Distutils 'install' command."""
       4  
       5  import sys
       6  import sysconfig
       7  import os
       8  import re
       9  
      10  from distutils import log
      11  from distutils.core import Command
      12  from distutils.debug import DEBUG
      13  from distutils.sysconfig import get_config_vars
      14  from distutils.errors import DistutilsPlatformError
      15  from distutils.file_util import write_file
      16  from distutils.util import convert_path, subst_vars, change_root
      17  from distutils.util import get_platform
      18  from distutils.errors import DistutilsOptionError
      19  
      20  from site import USER_BASE
      21  from site import USER_SITE
      22  
      23  HAS_USER_SITE = (USER_SITE is not None)
      24  
      25  # The keys to an installation scheme; if any new types of files are to be
      26  # installed, be sure to add an entry to every scheme in
      27  # sysconfig._INSTALL_SCHEMES, and to SCHEME_KEYS here.
      28  SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data')
      29  
      30  # The following code provides backward-compatible INSTALL_SCHEMES
      31  # while making the sysconfig module the single point of truth.
      32  # This makes it easier for OS distributions where they need to
      33  # alter locations for packages installations in a single place.
      34  # Note that this module is deprecated (PEP 632); all consumers
      35  # of this information should switch to using sysconfig directly.
      36  INSTALL_SCHEMES = {"unix_prefix": {}, "unix_home": {}, "nt": {}}
      37  
      38  # Copy from sysconfig._INSTALL_SCHEMES
      39  for key in SCHEME_KEYS:
      40      for distutils_scheme_name, sys_scheme_name in (
      41              ("unix_prefix", "posix_prefix"), ("unix_home", "posix_home"),
      42              ("nt", "nt")):
      43          sys_key = key
      44          sys_scheme = sysconfig._INSTALL_SCHEMES[sys_scheme_name]
      45          if key == "headers" and key not in sys_scheme:
      46              # On POSIX-y platforms, Python will:
      47              # - Build from .h files in 'headers' (only there when
      48              #   building CPython)
      49              # - Install .h files to 'include'
      50              # When 'headers' is missing, fall back to 'include'
      51              sys_key = 'include'
      52          INSTALL_SCHEMES[distutils_scheme_name][key] = sys_scheme[sys_key]
      53  
      54  # Transformation to different template format
      55  for main_key in INSTALL_SCHEMES:
      56      for key, value in INSTALL_SCHEMES[main_key].items():
      57          # Change all ocurences of {variable} to $variable
      58          value = re.sub(r"\{(.+?)\}", r"$\g<1>", value)
      59          value = value.replace("$installed_base", "$base")
      60          value = value.replace("$py_version_nodot_plat", "$py_version_nodot")
      61          if key == "headers":
      62              value += "/$dist_name"
      63          if sys.version_info >= (3, 9) and key == "platlib":
      64              # platlibdir is available since 3.9: bpo-1294959
      65              value = value.replace("/lib/", "/$platlibdir/")
      66          INSTALL_SCHEMES[main_key][key] = value
      67  
      68  # The following part of INSTALL_SCHEMES has a different definition
      69  # than the one in sysconfig, but because both depend on the site module,
      70  # the outcomes should be the same.
      71  if HAS_USER_SITE:
      72      INSTALL_SCHEMES['nt_user'] = {
      73          'purelib': '$usersite',
      74          'platlib': '$usersite',
      75          'headers': '$userbase/Python$py_version_nodot/Include/$dist_name',
      76          'scripts': '$userbase/Python$py_version_nodot/Scripts',
      77          'data'   : '$userbase',
      78          }
      79  
      80      INSTALL_SCHEMES['unix_user'] = {
      81          'purelib': '$usersite',
      82          'platlib': '$usersite',
      83          'headers':
      84              '$userbase/include/python$py_version_short$abiflags/$dist_name',
      85          'scripts': '$userbase/bin',
      86          'data'   : '$userbase',
      87          }
      88  
      89  
      90  class ESC[4;38;5;81minstall(ESC[4;38;5;149mCommand):
      91  
      92      description = "install everything from build directory"
      93  
      94      user_options = [
      95          # Select installation scheme and set base director(y|ies)
      96          ('prefix=', None,
      97           "installation prefix"),
      98          ('exec-prefix=', None,
      99           "(Unix only) prefix for platform-specific files"),
     100          ('home=', None,
     101           "(Unix only) home directory to install under"),
     102  
     103          # Or, just set the base director(y|ies)
     104          ('install-base=', None,
     105           "base installation directory (instead of --prefix or --home)"),
     106          ('install-platbase=', None,
     107           "base installation directory for platform-specific files " +
     108           "(instead of --exec-prefix or --home)"),
     109          ('root=', None,
     110           "install everything relative to this alternate root directory"),
     111  
     112          # Or, explicitly set the installation scheme
     113          ('install-purelib=', None,
     114           "installation directory for pure Python module distributions"),
     115          ('install-platlib=', None,
     116           "installation directory for non-pure module distributions"),
     117          ('install-lib=', None,
     118           "installation directory for all module distributions " +
     119           "(overrides --install-purelib and --install-platlib)"),
     120  
     121          ('install-headers=', None,
     122           "installation directory for C/C++ headers"),
     123          ('install-scripts=', None,
     124           "installation directory for Python scripts"),
     125          ('install-data=', None,
     126           "installation directory for data files"),
     127  
     128          # Byte-compilation options -- see install_lib.py for details, as
     129          # these are duplicated from there (but only install_lib does
     130          # anything with them).
     131          ('compile', 'c', "compile .py to .pyc [default]"),
     132          ('no-compile', None, "don't compile .py files"),
     133          ('optimize=', 'O',
     134           "also compile with optimization: -O1 for \"python -O\", "
     135           "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
     136  
     137          # Miscellaneous control options
     138          ('force', 'f',
     139           "force installation (overwrite any existing files)"),
     140          ('skip-build', None,
     141           "skip rebuilding everything (for testing/debugging)"),
     142  
     143          # Where to install documentation (eventually!)
     144          #('doc-format=', None, "format of documentation to generate"),
     145          #('install-man=', None, "directory for Unix man pages"),
     146          #('install-html=', None, "directory for HTML documentation"),
     147          #('install-info=', None, "directory for GNU info files"),
     148  
     149          ('record=', None,
     150           "filename in which to record list of installed files"),
     151          ]
     152  
     153      boolean_options = ['compile', 'force', 'skip-build']
     154  
     155      if HAS_USER_SITE:
     156          user_options.append(('user', None,
     157                               "install in user site-package '%s'" % USER_SITE))
     158          boolean_options.append('user')
     159  
     160      negative_opt = {'no-compile' : 'compile'}
     161  
     162  
     163      def initialize_options(self):
     164          """Initializes options."""
     165          # High-level options: these select both an installation base
     166          # and scheme.
     167          self.prefix = None
     168          self.exec_prefix = None
     169          self.home = None
     170          self.user = 0
     171  
     172          # These select only the installation base; it's up to the user to
     173          # specify the installation scheme (currently, that means supplying
     174          # the --install-{platlib,purelib,scripts,data} options).
     175          self.install_base = None
     176          self.install_platbase = None
     177          self.root = None
     178  
     179          # These options are the actual installation directories; if not
     180          # supplied by the user, they are filled in using the installation
     181          # scheme implied by prefix/exec-prefix/home and the contents of
     182          # that installation scheme.
     183          self.install_purelib = None     # for pure module distributions
     184          self.install_platlib = None     # non-pure (dists w/ extensions)
     185          self.install_headers = None     # for C/C++ headers
     186          self.install_lib = None         # set to either purelib or platlib
     187          self.install_scripts = None
     188          self.install_data = None
     189          if HAS_USER_SITE:
     190              self.install_userbase = USER_BASE
     191              self.install_usersite = USER_SITE
     192  
     193          self.compile = None
     194          self.optimize = None
     195  
     196          # Deprecated
     197          # These two are for putting non-packagized distributions into their
     198          # own directory and creating a .pth file if it makes sense.
     199          # 'extra_path' comes from the setup file; 'install_path_file' can
     200          # be turned off if it makes no sense to install a .pth file.  (But
     201          # better to install it uselessly than to guess wrong and not
     202          # install it when it's necessary and would be used!)  Currently,
     203          # 'install_path_file' is always true unless some outsider meddles
     204          # with it.
     205          self.extra_path = None
     206          self.install_path_file = 1
     207  
     208          # 'force' forces installation, even if target files are not
     209          # out-of-date.  'skip_build' skips running the "build" command,
     210          # handy if you know it's not necessary.  'warn_dir' (which is *not*
     211          # a user option, it's just there so the bdist_* commands can turn
     212          # it off) determines whether we warn about installing to a
     213          # directory not in sys.path.
     214          self.force = 0
     215          self.skip_build = 0
     216          self.warn_dir = 1
     217  
     218          # These are only here as a conduit from the 'build' command to the
     219          # 'install_*' commands that do the real work.  ('build_base' isn't
     220          # actually used anywhere, but it might be useful in future.)  They
     221          # are not user options, because if the user told the install
     222          # command where the build directory is, that wouldn't affect the
     223          # build command.
     224          self.build_base = None
     225          self.build_lib = None
     226  
     227          # Not defined yet because we don't know anything about
     228          # documentation yet.
     229          #self.install_man = None
     230          #self.install_html = None
     231          #self.install_info = None
     232  
     233          self.record = None
     234  
     235  
     236      # -- Option finalizing methods -------------------------------------
     237      # (This is rather more involved than for most commands,
     238      # because this is where the policy for installing third-
     239      # party Python modules on various platforms given a wide
     240      # array of user input is decided.  Yes, it's quite complex!)
     241  
     242      def finalize_options(self):
     243          """Finalizes options."""
     244          # This method (and its helpers, like 'finalize_unix()',
     245          # 'finalize_other()', and 'select_scheme()') is where the default
     246          # installation directories for modules, extension modules, and
     247          # anything else we care to install from a Python module
     248          # distribution.  Thus, this code makes a pretty important policy
     249          # statement about how third-party stuff is added to a Python
     250          # installation!  Note that the actual work of installation is done
     251          # by the relatively simple 'install_*' commands; they just take
     252          # their orders from the installation directory options determined
     253          # here.
     254  
     255          # Check for errors/inconsistencies in the options; first, stuff
     256          # that's wrong on any platform.
     257  
     258          if ((self.prefix or self.exec_prefix or self.home) and
     259              (self.install_base or self.install_platbase)):
     260              raise DistutilsOptionError(
     261                     "must supply either prefix/exec-prefix/home or " +
     262                     "install-base/install-platbase -- not both")
     263  
     264          if self.home and (self.prefix or self.exec_prefix):
     265              raise DistutilsOptionError(
     266                    "must supply either home or prefix/exec-prefix -- not both")
     267  
     268          if self.user and (self.prefix or self.exec_prefix or self.home or
     269                  self.install_base or self.install_platbase):
     270              raise DistutilsOptionError("can't combine user with prefix, "
     271                                         "exec_prefix/home, or install_(plat)base")
     272  
     273          # Next, stuff that's wrong (or dubious) only on certain platforms.
     274          if os.name != "posix":
     275              if self.exec_prefix:
     276                  self.warn("exec-prefix option ignored on this platform")
     277                  self.exec_prefix = None
     278  
     279          # Now the interesting logic -- so interesting that we farm it out
     280          # to other methods.  The goal of these methods is to set the final
     281          # values for the install_{lib,scripts,data,...}  options, using as
     282          # input a heady brew of prefix, exec_prefix, home, install_base,
     283          # install_platbase, user-supplied versions of
     284          # install_{purelib,platlib,lib,scripts,data,...}, and the
     285          # INSTALL_SCHEME dictionary above.  Phew!
     286  
     287          self.dump_dirs("pre-finalize_{unix,other}")
     288  
     289          if os.name == 'posix':
     290              self.finalize_unix()
     291          else:
     292              self.finalize_other()
     293  
     294          self.dump_dirs("post-finalize_{unix,other}()")
     295  
     296          # Expand configuration variables, tilde, etc. in self.install_base
     297          # and self.install_platbase -- that way, we can use $base or
     298          # $platbase in the other installation directories and not worry
     299          # about needing recursive variable expansion (shudder).
     300  
     301          py_version = sys.version.split()[0]
     302          (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix')
     303          try:
     304              abiflags = sys.abiflags
     305          except AttributeError:
     306              # sys.abiflags may not be defined on all platforms.
     307              abiflags = ''
     308          self.config_vars = {'dist_name': self.distribution.get_name(),
     309                              'dist_version': self.distribution.get_version(),
     310                              'dist_fullname': self.distribution.get_fullname(),
     311                              'py_version': py_version,
     312                              'py_version_short': '%d.%d' % sys.version_info[:2],
     313                              'py_version_nodot': '%d%d' % sys.version_info[:2],
     314                              'sys_prefix': prefix,
     315                              'prefix': prefix,
     316                              'sys_exec_prefix': exec_prefix,
     317                              'exec_prefix': exec_prefix,
     318                              'abiflags': abiflags,
     319                              'platlibdir': sys.platlibdir,
     320                             }
     321  
     322          if HAS_USER_SITE:
     323              self.config_vars['userbase'] = self.install_userbase
     324              self.config_vars['usersite'] = self.install_usersite
     325  
     326          if sysconfig.is_python_build(True):
     327              self.config_vars['srcdir'] = sysconfig.get_config_var('srcdir')
     328  
     329          self.expand_basedirs()
     330  
     331          self.dump_dirs("post-expand_basedirs()")
     332  
     333          # Now define config vars for the base directories so we can expand
     334          # everything else.
     335          self.config_vars['base'] = self.install_base
     336          self.config_vars['platbase'] = self.install_platbase
     337  
     338          if DEBUG:
     339              from pprint import pprint
     340              print("config vars:")
     341              pprint(self.config_vars)
     342  
     343          # Expand "~" and configuration variables in the installation
     344          # directories.
     345          self.expand_dirs()
     346  
     347          self.dump_dirs("post-expand_dirs()")
     348  
     349          # Create directories in the home dir:
     350          if self.user:
     351              self.create_home_path()
     352  
     353          # Pick the actual directory to install all modules to: either
     354          # install_purelib or install_platlib, depending on whether this
     355          # module distribution is pure or not.  Of course, if the user
     356          # already specified install_lib, use their selection.
     357          if self.install_lib is None:
     358              if self.distribution.ext_modules: # has extensions: non-pure
     359                  self.install_lib = self.install_platlib
     360              else:
     361                  self.install_lib = self.install_purelib
     362  
     363  
     364          # Convert directories from Unix /-separated syntax to the local
     365          # convention.
     366          self.convert_paths('lib', 'purelib', 'platlib',
     367                             'scripts', 'data', 'headers')
     368          if HAS_USER_SITE:
     369              self.convert_paths('userbase', 'usersite')
     370  
     371          # Deprecated
     372          # Well, we're not actually fully completely finalized yet: we still
     373          # have to deal with 'extra_path', which is the hack for allowing
     374          # non-packagized module distributions (hello, Numerical Python!) to
     375          # get their own directories.
     376          self.handle_extra_path()
     377          self.install_libbase = self.install_lib # needed for .pth file
     378          self.install_lib = os.path.join(self.install_lib, self.extra_dirs)
     379  
     380          # If a new root directory was supplied, make all the installation
     381          # dirs relative to it.
     382          if self.root is not None:
     383              self.change_roots('libbase', 'lib', 'purelib', 'platlib',
     384                                'scripts', 'data', 'headers')
     385  
     386          self.dump_dirs("after prepending root")
     387  
     388          # Find out the build directories, ie. where to install from.
     389          self.set_undefined_options('build',
     390                                     ('build_base', 'build_base'),
     391                                     ('build_lib', 'build_lib'))
     392  
     393          # Punt on doc directories for now -- after all, we're punting on
     394          # documentation completely!
     395  
     396      def dump_dirs(self, msg):
     397          """Dumps the list of user options."""
     398          if not DEBUG:
     399              return
     400          from distutils.fancy_getopt import longopt_xlate
     401          log.debug(msg + ":")
     402          for opt in self.user_options:
     403              opt_name = opt[0]
     404              if opt_name[-1] == "=":
     405                  opt_name = opt_name[0:-1]
     406              if opt_name in self.negative_opt:
     407                  opt_name = self.negative_opt[opt_name]
     408                  opt_name = opt_name.translate(longopt_xlate)
     409                  val = not getattr(self, opt_name)
     410              else:
     411                  opt_name = opt_name.translate(longopt_xlate)
     412                  val = getattr(self, opt_name)
     413              log.debug("  %s: %s", opt_name, val)
     414  
     415      def finalize_unix(self):
     416          """Finalizes options for posix platforms."""
     417          if self.install_base is not None or self.install_platbase is not None:
     418              if ((self.install_lib is None and
     419                   self.install_purelib is None and
     420                   self.install_platlib is None) or
     421                  self.install_headers is None or
     422                  self.install_scripts is None or
     423                  self.install_data is None):
     424                  raise DistutilsOptionError(
     425                        "install-base or install-platbase supplied, but "
     426                        "installation scheme is incomplete")
     427              return
     428  
     429          if self.user:
     430              if self.install_userbase is None:
     431                  raise DistutilsPlatformError(
     432                      "User base directory is not specified")
     433              self.install_base = self.install_platbase = self.install_userbase
     434              self.select_scheme("unix_user")
     435          elif self.home is not None:
     436              self.install_base = self.install_platbase = self.home
     437              self.select_scheme("unix_home")
     438          else:
     439              if self.prefix is None:
     440                  if self.exec_prefix is not None:
     441                      raise DistutilsOptionError(
     442                            "must not supply exec-prefix without prefix")
     443  
     444                  self.prefix = os.path.normpath(sys.prefix)
     445                  self.exec_prefix = os.path.normpath(sys.exec_prefix)
     446  
     447              else:
     448                  if self.exec_prefix is None:
     449                      self.exec_prefix = self.prefix
     450  
     451              self.install_base = self.prefix
     452              self.install_platbase = self.exec_prefix
     453              self.select_scheme("unix_prefix")
     454  
     455      def finalize_other(self):
     456          """Finalizes options for non-posix platforms"""
     457          if self.user:
     458              if self.install_userbase is None:
     459                  raise DistutilsPlatformError(
     460                      "User base directory is not specified")
     461              self.install_base = self.install_platbase = self.install_userbase
     462              self.select_scheme(os.name + "_user")
     463          elif self.home is not None:
     464              self.install_base = self.install_platbase = self.home
     465              self.select_scheme("unix_home")
     466          else:
     467              if self.prefix is None:
     468                  self.prefix = os.path.normpath(sys.prefix)
     469  
     470              self.install_base = self.install_platbase = self.prefix
     471              try:
     472                  self.select_scheme(os.name)
     473              except KeyError:
     474                  raise DistutilsPlatformError(
     475                        "I don't know how to install stuff on '%s'" % os.name)
     476  
     477      def select_scheme(self, name):
     478          """Sets the install directories by applying the install schemes."""
     479          # it's the caller's problem if they supply a bad name!
     480          scheme = INSTALL_SCHEMES[name]
     481          for key in SCHEME_KEYS:
     482              attrname = 'install_' + key
     483              if getattr(self, attrname) is None:
     484                  setattr(self, attrname, scheme[key])
     485  
     486      def _expand_attrs(self, attrs):
     487          for attr in attrs:
     488              val = getattr(self, attr)
     489              if val is not None:
     490                  if os.name == 'posix' or os.name == 'nt':
     491                      val = os.path.expanduser(val)
     492                  val = subst_vars(val, self.config_vars)
     493                  setattr(self, attr, val)
     494  
     495      def expand_basedirs(self):
     496          """Calls `os.path.expanduser` on install_base, install_platbase and
     497          root."""
     498          self._expand_attrs(['install_base', 'install_platbase', 'root'])
     499  
     500      def expand_dirs(self):
     501          """Calls `os.path.expanduser` on install dirs."""
     502          self._expand_attrs(['install_purelib', 'install_platlib',
     503                              'install_lib', 'install_headers',
     504                              'install_scripts', 'install_data',])
     505  
     506      def convert_paths(self, *names):
     507          """Call `convert_path` over `names`."""
     508          for name in names:
     509              attr = "install_" + name
     510              setattr(self, attr, convert_path(getattr(self, attr)))
     511  
     512      def handle_extra_path(self):
     513          """Set `path_file` and `extra_dirs` using `extra_path`."""
     514          if self.extra_path is None:
     515              self.extra_path = self.distribution.extra_path
     516  
     517          if self.extra_path is not None:
     518              log.warn(
     519                  "Distribution option extra_path is deprecated. "
     520                  "See issue27919 for details."
     521              )
     522              if isinstance(self.extra_path, str):
     523                  self.extra_path = self.extra_path.split(',')
     524  
     525              if len(self.extra_path) == 1:
     526                  path_file = extra_dirs = self.extra_path[0]
     527              elif len(self.extra_path) == 2:
     528                  path_file, extra_dirs = self.extra_path
     529              else:
     530                  raise DistutilsOptionError(
     531                        "'extra_path' option must be a list, tuple, or "
     532                        "comma-separated string with 1 or 2 elements")
     533  
     534              # convert to local form in case Unix notation used (as it
     535              # should be in setup scripts)
     536              extra_dirs = convert_path(extra_dirs)
     537          else:
     538              path_file = None
     539              extra_dirs = ''
     540  
     541          # XXX should we warn if path_file and not extra_dirs? (in which
     542          # case the path file would be harmless but pointless)
     543          self.path_file = path_file
     544          self.extra_dirs = extra_dirs
     545  
     546      def change_roots(self, *names):
     547          """Change the install directories pointed by name using root."""
     548          for name in names:
     549              attr = "install_" + name
     550              setattr(self, attr, change_root(self.root, getattr(self, attr)))
     551  
     552      def create_home_path(self):
     553          """Create directories under ~."""
     554          if not self.user:
     555              return
     556          home = convert_path(os.path.expanduser("~"))
     557          for name, path in self.config_vars.items():
     558              if path.startswith(home) and not os.path.isdir(path):
     559                  self.debug_print("os.makedirs('%s', 0o700)" % path)
     560                  os.makedirs(path, 0o700)
     561  
     562      # -- Command execution methods -------------------------------------
     563  
     564      def run(self):
     565          """Runs the command."""
     566          # Obviously have to build before we can install
     567          if not self.skip_build:
     568              self.run_command('build')
     569              # If we built for any other platform, we can't install.
     570              build_plat = self.distribution.get_command_obj('build').plat_name
     571              # check warn_dir - it is a clue that the 'install' is happening
     572              # internally, and not to sys.path, so we don't check the platform
     573              # matches what we are running.
     574              if self.warn_dir and build_plat != get_platform():
     575                  raise DistutilsPlatformError("Can't install when "
     576                                               "cross-compiling")
     577  
     578          # Run all sub-commands (at least those that need to be run)
     579          for cmd_name in self.get_sub_commands():
     580              self.run_command(cmd_name)
     581  
     582          if self.path_file:
     583              self.create_path_file()
     584  
     585          # write list of installed files, if requested.
     586          if self.record:
     587              outputs = self.get_outputs()
     588              if self.root:               # strip any package prefix
     589                  root_len = len(self.root)
     590                  for counter in range(len(outputs)):
     591                      outputs[counter] = outputs[counter][root_len:]
     592              self.execute(write_file,
     593                           (self.record, outputs),
     594                           "writing list of installed files to '%s'" %
     595                           self.record)
     596  
     597          sys_path = map(os.path.normpath, sys.path)
     598          sys_path = map(os.path.normcase, sys_path)
     599          install_lib = os.path.normcase(os.path.normpath(self.install_lib))
     600          if (self.warn_dir and
     601              not (self.path_file and self.install_path_file) and
     602              install_lib not in sys_path):
     603              log.debug(("modules installed to '%s', which is not in "
     604                         "Python's module search path (sys.path) -- "
     605                         "you'll have to change the search path yourself"),
     606                         self.install_lib)
     607  
     608      def create_path_file(self):
     609          """Creates the .pth file"""
     610          filename = os.path.join(self.install_libbase,
     611                                  self.path_file + ".pth")
     612          if self.install_path_file:
     613              self.execute(write_file,
     614                           (filename, [self.extra_dirs]),
     615                           "creating %s" % filename)
     616          else:
     617              self.warn("path file '%s' not created" % filename)
     618  
     619  
     620      # -- Reporting methods ---------------------------------------------
     621  
     622      def get_outputs(self):
     623          """Assembles the outputs of all the sub-commands."""
     624          outputs = []
     625          for cmd_name in self.get_sub_commands():
     626              cmd = self.get_finalized_command(cmd_name)
     627              # Add the contents of cmd.get_outputs(), ensuring
     628              # that outputs doesn't contain duplicate entries
     629              for filename in cmd.get_outputs():
     630                  if filename not in outputs:
     631                      outputs.append(filename)
     632  
     633          if self.path_file and self.install_path_file:
     634              outputs.append(os.path.join(self.install_libbase,
     635                                          self.path_file + ".pth"))
     636  
     637          return outputs
     638  
     639      def get_inputs(self):
     640          """Returns the inputs of all the sub-commands"""
     641          # XXX gee, this looks familiar ;-(
     642          inputs = []
     643          for cmd_name in self.get_sub_commands():
     644              cmd = self.get_finalized_command(cmd_name)
     645              inputs.extend(cmd.get_inputs())
     646  
     647          return inputs
     648  
     649      # -- Predicates for sub-command list -------------------------------
     650  
     651      def has_lib(self):
     652          """Returns true if the current distribution has any Python
     653          modules to install."""
     654          return (self.distribution.has_pure_modules() or
     655                  self.distribution.has_ext_modules())
     656  
     657      def has_headers(self):
     658          """Returns true if the current distribution has any headers to
     659          install."""
     660          return self.distribution.has_headers()
     661  
     662      def has_scripts(self):
     663          """Returns true if the current distribution has any scripts to.
     664          install."""
     665          return self.distribution.has_scripts()
     666  
     667      def has_data(self):
     668          """Returns true if the current distribution has any data to.
     669          install."""
     670          return self.distribution.has_data_files()
     671  
     672      # 'sub_commands': a list of commands this command might have to run to
     673      # get its work done.  See cmd.py for more info.
     674      sub_commands = [('install_lib',     has_lib),
     675                      ('install_headers', has_headers),
     676                      ('install_scripts', has_scripts),
     677                      ('install_data',    has_data),
     678                      ('install_egg_info', lambda self:True),
     679                     ]