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