1"""distutils.command.install 2 3Implements the Distutils 'install' command.""" 4 5import sys 6import sysconfig 7import os 8import re 9 10from distutils import log 11from distutils.core import Command 12from distutils.debug import DEBUG 13from distutils.sysconfig import get_config_vars 14from distutils.errors import DistutilsPlatformError 15from distutils.file_util import write_file 16from distutils.util import convert_path, subst_vars, change_root 17from distutils.util import get_platform 18from distutils.errors import DistutilsOptionError 19 20from site import USER_BASE 21from site import USER_SITE 22 23HAS_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. 28SCHEME_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. 36INSTALL_SCHEMES = {"unix_prefix": {}, "unix_home": {}, "nt": {}} 37 38# Copy from sysconfig._INSTALL_SCHEMES 39for 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 55for 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. 71if 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 90class install(Command): 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 ] 680