diff options
Diffstat (limited to 'Lib/sysconfig.py')
-rw-r--r-- | Lib/sysconfig.py | 407 |
1 files changed, 179 insertions, 228 deletions
diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py index b2183d8..71da1db 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -1,8 +1,8 @@ -"""Provide access to Python's configuration information. +"""Access to Python's configuration information.""" -""" -import sys import os +import re +import sys from os.path import pardir, realpath __all__ = [ @@ -17,50 +17,50 @@ __all__ = [ 'get_python_version', 'get_scheme_names', 'parse_config_h', - ] +] _INSTALL_SCHEMES = { 'posix_prefix': { - 'stdlib': '{base}/lib/python{py_version_short}', + 'stdlib': '{installed_base}/lib/python{py_version_short}', 'platstdlib': '{platbase}/lib/python{py_version_short}', 'purelib': '{base}/lib/python{py_version_short}/site-packages', 'platlib': '{platbase}/lib/python{py_version_short}/site-packages', 'include': - '{base}/include/python{py_version_short}{abiflags}', + '{installed_base}/include/python{py_version_short}{abiflags}', 'platinclude': - '{platbase}/include/python{py_version_short}{abiflags}', + '{installed_platbase}/include/python{py_version_short}{abiflags}', 'scripts': '{base}/bin', 'data': '{base}', }, 'posix_home': { - 'stdlib': '{base}/lib/python', + 'stdlib': '{installed_base}/lib/python', 'platstdlib': '{base}/lib/python', 'purelib': '{base}/lib/python', 'platlib': '{base}/lib/python', - 'include': '{base}/include/python', - 'platinclude': '{base}/include/python', + 'include': '{installed_base}/include/python', + 'platinclude': '{installed_base}/include/python', 'scripts': '{base}/bin', - 'data' : '{base}', + 'data': '{base}', }, 'nt': { - 'stdlib': '{base}/Lib', + 'stdlib': '{installed_base}/Lib', 'platstdlib': '{base}/Lib', 'purelib': '{base}/Lib/site-packages', 'platlib': '{base}/Lib/site-packages', - 'include': '{base}/Include', - 'platinclude': '{base}/Include', + 'include': '{installed_base}/Include', + 'platinclude': '{installed_base}/Include', 'scripts': '{base}/Scripts', - 'data' : '{base}', + 'data': '{base}', }, 'os2': { - 'stdlib': '{base}/Lib', + 'stdlib': '{installed_base}/Lib', 'platstdlib': '{base}/Lib', 'purelib': '{base}/Lib/site-packages', 'platlib': '{base}/Lib/site-packages', - 'include': '{base}/Include', - 'platinclude': '{base}/Include', + 'include': '{installed_base}/Include', + 'platinclude': '{installed_base}/Include', 'scripts': '{base}/Scripts', - 'data' : '{base}', + 'data': '{base}', }, 'os2_home': { 'stdlib': '{userbase}/lib/python{py_version_short}', @@ -69,7 +69,7 @@ _INSTALL_SCHEMES = { 'platlib': '{userbase}/lib/python{py_version_short}/site-packages', 'include': '{userbase}/include/python{py_version_short}', 'scripts': '{userbase}/bin', - 'data' : '{userbase}', + 'data': '{userbase}', }, 'nt_user': { 'stdlib': '{userbase}/Python{py_version_nodot}', @@ -78,7 +78,7 @@ _INSTALL_SCHEMES = { 'platlib': '{userbase}/Python{py_version_nodot}/site-packages', 'include': '{userbase}/Python{py_version_nodot}/Include', 'scripts': '{userbase}/Scripts', - 'data' : '{userbase}', + 'data': '{userbase}', }, 'posix_user': { 'stdlib': '{userbase}/lib/python{py_version_short}', @@ -87,7 +87,7 @@ _INSTALL_SCHEMES = { 'platlib': '{userbase}/lib/python{py_version_short}/site-packages', 'include': '{userbase}/include/python{py_version_short}', 'scripts': '{userbase}/bin', - 'data' : '{userbase}', + 'data': '{userbase}', }, 'osx_framework_user': { 'stdlib': '{userbase}/lib/python', @@ -96,20 +96,26 @@ _INSTALL_SCHEMES = { 'platlib': '{userbase}/lib/python/site-packages', 'include': '{userbase}/include', 'scripts': '{userbase}/bin', - 'data' : '{userbase}', + 'data': '{userbase}', }, } _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include', 'scripts', 'data') + + # FIXME don't rely on sys.version here, its format is an implementation detail + # of CPython, use sys.version_info or sys.hexversion _PY_VERSION = sys.version.split()[0] _PY_VERSION_SHORT = sys.version[:3] _PY_VERSION_SHORT_NO_DOT = _PY_VERSION[0] + _PY_VERSION[2] _PREFIX = os.path.normpath(sys.prefix) +_BASE_PREFIX = os.path.normpath(sys.base_prefix) _EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +_BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) _CONFIG_VARS = None _USER_BASE = None + def _safe_realpath(path): try: return realpath(path) @@ -132,19 +138,35 @@ if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower(): if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower(): _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) -def is_python_build(): +# set for cross builds +if "_PYTHON_PROJECT_BASE" in os.environ: + _PROJECT_BASE = _safe_realpath(os.environ["_PYTHON_PROJECT_BASE"]) + +def _is_python_source_dir(d): for fn in ("Setup.dist", "Setup.local"): - if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)): + if os.path.isfile(os.path.join(d, "Modules", fn)): return True return False -_PYTHON_BUILD = is_python_build() +_sys_home = getattr(sys, '_home', None) +if _sys_home and os.name == 'nt' and \ + _sys_home.lower().endswith(('pcbuild', 'pcbuild\\amd64')): + _sys_home = os.path.dirname(_sys_home) + if _sys_home.endswith('pcbuild'): # must be amd64 + _sys_home = os.path.dirname(_sys_home) +def is_python_build(check_home=False): + if check_home and _sys_home: + return _is_python_source_dir(_sys_home) + return _is_python_source_dir(_PROJECT_BASE) + +_PYTHON_BUILD = is_python_build(True) if _PYTHON_BUILD: for scheme in ('posix_prefix', 'posix_home'): _INSTALL_SCHEMES[scheme]['include'] = '{srcdir}/Include' _INSTALL_SCHEMES[scheme]['platinclude'] = '{projectbase}/.' + def _subst_vars(s, local_vars): try: return s.format(**local_vars) @@ -161,6 +183,7 @@ def _extend_dict(target_dict, other_dict): continue target_dict[key] = value + def _expand_vars(scheme, vars): res = {} if vars is None: @@ -173,29 +196,41 @@ def _expand_vars(scheme, vars): res[key] = os.path.normpath(_subst_vars(value, vars)) return res + def _get_default_scheme(): if os.name == 'posix': # the default scheme for posix is posix_prefix return 'posix_prefix' return os.name + def _getuserbase(): env_base = os.environ.get("PYTHONUSERBASE", None) + def joinuser(*args): return os.path.expanduser(os.path.join(*args)) # what about 'os2emx', 'riscos' ? if os.name == "nt": base = os.environ.get("APPDATA") or "~" - return env_base if env_base else joinuser(base, "Python") + if env_base: + return env_base + else: + return joinuser(base, "Python") if sys.platform == "darwin": framework = get_config_var("PYTHONFRAMEWORK") if framework: - return env_base if env_base else joinuser("~", "Library", framework, "%d.%d"%( - sys.version_info[:2])) + if env_base: + return env_base + else: + return joinuser("~", "Library", framework, "%d.%d" % + sys.version_info[:2]) - return env_base if env_base else joinuser("~", ".local") + if env_base: + return env_base + else: + return joinuser("~", ".local") def _parse_makefile(filename, vars=None): @@ -205,7 +240,6 @@ def _parse_makefile(filename, vars=None): optional dictionary is passed in as the second argument, it is used instead of a new dictionary. """ - import re # Regexes needed for parsing Makefile (and similar syntaxes, # like old-style Setup files). _variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") @@ -267,7 +301,8 @@ def _parse_makefile(filename, vars=None): item = os.environ[n] elif n in renamed_variables: - if name.startswith('PY_') and name[3:] in renamed_variables: + if (name.startswith('PY_') and + name[3:] in renamed_variables): item = "" elif 'PY_' + n in notdone: @@ -300,7 +335,6 @@ def _parse_makefile(filename, vars=None): if name not in done: done[name] = value - else: # bogus variable reference (e.g. "prefix=$/opt/python"); # just drop it since we can't deal @@ -320,14 +354,17 @@ def _parse_makefile(filename, vars=None): def get_makefile_filename(): """Return the path of the Makefile.""" if _PYTHON_BUILD: - return os.path.join(_PROJECT_BASE, "Makefile") - return os.path.join(get_path('stdlib'), - 'config-{}{}'.format(_PY_VERSION_SHORT, sys.abiflags), - 'Makefile') - + return os.path.join(_sys_home or _PROJECT_BASE, "Makefile") + if hasattr(sys, 'abiflags'): + config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags) + else: + config_dir_name = 'config' + return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') -def _init_posix(vars): - """Initialize the module as appropriate for POSIX systems.""" +def _generate_posix_vars(): + """Generate the Python module containing build-time variables.""" + import pprint + vars = {} # load the installed Makefile: makefile = get_makefile_filename() try: @@ -353,6 +390,46 @@ def _init_posix(vars): if _PYTHON_BUILD: vars['LDSHARED'] = vars['BLDSHARED'] + # There's a chicken-and-egg situation on OS X with regards to the + # _sysconfigdata module after the changes introduced by #15298: + # get_config_vars() is called by get_platform() as part of the + # `make pybuilddir.txt` target -- which is a precursor to the + # _sysconfigdata.py module being constructed. Unfortunately, + # get_config_vars() eventually calls _init_posix(), which attempts + # to import _sysconfigdata, which we won't have built yet. In order + # for _init_posix() to work, if we're on Darwin, just mock up the + # _sysconfigdata module manually and populate it with the build vars. + # This is more than sufficient for ensuring the subsequent call to + # get_platform() succeeds. + name = '_sysconfigdata' + if 'darwin' in sys.platform: + import imp + module = imp.new_module(name) + module.build_time_vars = vars + sys.modules[name] = module + + pybuilddir = 'build/lib.%s-%s' % (get_platform(), sys.version[:3]) + if hasattr(sys, "gettotalrefcount"): + pybuilddir += '-pydebug' + os.makedirs(pybuilddir, exist_ok=True) + destfile = os.path.join(pybuilddir, name + '.py') + + with open(destfile, 'w', encoding='utf8') as f: + f.write('# system configuration generated and used by' + ' the sysconfig module\n') + f.write('build_time_vars = ') + pprint.pprint(vars, stream=f) + + # Create file used for sys.path fixup -- see Modules/getpath.c + with open('pybuilddir.txt', 'w', encoding='ascii') as f: + f.write(pybuilddir) + +def _init_posix(vars): + """Initialize the module as appropriate for POSIX systems.""" + # _sysconfigdata is generated at build time, see _generate_posix_vars() + from _sysconfigdata import build_time_vars + vars.update(build_time_vars) + def _init_non_posix(vars): """Initialize the module as appropriate for NT""" # set basic install directories @@ -376,7 +453,6 @@ def parse_config_h(fp, vars=None): optional dictionary is passed in as the second argument, it is used instead of a new dictionary. """ - import re if vars is None: vars = {} define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") @@ -389,8 +465,10 @@ def parse_config_h(fp, vars=None): m = define_rx.match(line) if m: n, v = m.group(1, 2) - try: v = int(v) - except ValueError: pass + try: + v = int(v) + except ValueError: + pass vars[n] = v else: m = undef_rx.match(line) @@ -398,27 +476,29 @@ def parse_config_h(fp, vars=None): vars[m.group(1)] = 0 return vars + def get_config_h_filename(): """Return the path of pyconfig.h.""" if _PYTHON_BUILD: if os.name == "nt": - inc_dir = os.path.join(_PROJECT_BASE, "PC") + inc_dir = os.path.join(_sys_home or _PROJECT_BASE, "PC") else: - inc_dir = _PROJECT_BASE + inc_dir = _sys_home or _PROJECT_BASE else: inc_dir = get_path('platinclude') return os.path.join(inc_dir, 'pyconfig.h') + def get_scheme_names(): """Return a tuple containing the schemes names.""" - schemes = list(_INSTALL_SCHEMES.keys()) - schemes.sort() - return tuple(schemes) + return tuple(sorted(_INSTALL_SCHEMES)) + def get_path_names(): """Return a tuple containing the paths names.""" return _SCHEME_KEYS + def get_paths(scheme=_get_default_scheme(), vars=None, expand=True): """Return a mapping containing an install scheme. @@ -430,6 +510,7 @@ def get_paths(scheme=_get_default_scheme(), vars=None, expand=True): else: return _INSTALL_SCHEMES[scheme] + def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True): """Return a path corresponding to the scheme. @@ -437,17 +518,17 @@ def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True): """ return get_paths(scheme, vars, expand)[name] + def get_config_vars(*args): """With no arguments, return a dictionary of all configuration variables relevant for the current platform. On Unix, this means every variable defined in Python's installed Makefile; - On Windows and Mac OS it's a much smaller set. + On Windows it's a much smaller set. With arguments, return a list of values that result from looking up each argument in the configuration variable dictionary. """ - import re global _CONFIG_VARS if _CONFIG_VARS is None: _CONFIG_VARS = {} @@ -459,7 +540,9 @@ def get_config_vars(*args): _CONFIG_VARS['py_version'] = _PY_VERSION _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT _CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2] + _CONFIG_VARS['installed_base'] = _BASE_PREFIX _CONFIG_VARS['base'] = _PREFIX + _CONFIG_VARS['installed_platbase'] = _BASE_EXEC_PREFIX _CONFIG_VARS['platbase'] = _EXEC_PREFIX _CONFIG_VARS['projectbase'] = _PROJECT_BASE try: @@ -477,88 +560,28 @@ def get_config_vars(*args): # the init-function. _CONFIG_VARS['userbase'] = _getuserbase() - if 'srcdir' not in _CONFIG_VARS: - _CONFIG_VARS['srcdir'] = _PROJECT_BASE - else: - _CONFIG_VARS['srcdir'] = _safe_realpath(_CONFIG_VARS['srcdir']) - - - # Convert srcdir into an absolute path if it appears necessary. - # Normally it is relative to the build directory. However, during - # testing, for example, we might be running a non-installed python - # from a different directory. - if _PYTHON_BUILD and os.name == "posix": - base = _PROJECT_BASE - try: - cwd = os.getcwd() - except OSError: - cwd = None - if (not os.path.isabs(_CONFIG_VARS['srcdir']) and - base != cwd): - # srcdir is relative and we are not in the same directory - # as the executable. Assume executable is in the build - # directory and make srcdir absolute. - srcdir = os.path.join(base, _CONFIG_VARS['srcdir']) - _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir) - - if sys.platform == 'darwin': - kernel_version = os.uname()[2] # Kernel version (8.4.3) - major_version = int(kernel_version.split('.')[0]) - - if major_version < 8: - # On Mac OS X before 10.4, check if -arch and -isysroot - # are in CFLAGS or LDFLAGS and remove them if they are. - # This is needed when building extensions on a 10.3 system - # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - flags = _CONFIG_VARS[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = re.sub('-isysroot [^ \t]*', ' ', flags) - _CONFIG_VARS[key] = flags + # Always convert srcdir to an absolute path + srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE) + if os.name == 'posix': + if _PYTHON_BUILD: + # If srcdir is a relative path (typically '.' or '..') + # then it should be interpreted relative to the directory + # containing Makefile. + base = os.path.dirname(get_makefile_filename()) + srcdir = os.path.join(base, srcdir) else: - # Allow the user to override the architecture flags using - # an environment variable. - # NOTE: This name was introduced by Apple in OSX 10.5 and - # is used by several scripting languages distributed with - # that OS release. - if 'ARCHFLAGS' in os.environ: - arch = os.environ['ARCHFLAGS'] - for key in ('LDFLAGS', 'BASECFLAGS', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _CONFIG_VARS[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = flags + ' ' + arch - _CONFIG_VARS[key] = flags - - # If we're on OSX 10.5 or later and the user tries to - # compiles an extension using an SDK that is not present - # on the current machine it is better to not use an SDK - # than to fail. - # - # The major usecase for this is users using a Python.org - # binary installer on OSX 10.6: that installer uses - # the 10.4u SDK, but that SDK is not installed by default - # when you install Xcode. - # - CFLAGS = _CONFIG_VARS.get('CFLAGS', '') - m = re.search('-isysroot\s+(\S+)', CFLAGS) - if m is not None: - sdk = m.group(1) - if not os.path.exists(sdk): - for key in ('LDFLAGS', 'BASECFLAGS', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _CONFIG_VARS[key] - flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags) - _CONFIG_VARS[key] = flags + # srcdir is not meaningful since the installation is + # spread about the filesystem. We choose the + # directory containing the Makefile since we know it + # exists. + srcdir = os.path.dirname(get_makefile_filename()) + _CONFIG_VARS['srcdir'] = _safe_realpath(srcdir) + + # OS X platforms require special customization to handle + # multi-architecture, multi-os-version installers + if sys.platform == 'darwin': + import _osx_support + _osx_support.customize_config_vars(_CONFIG_VARS) if args: vals = [] @@ -568,6 +591,7 @@ def get_config_vars(*args): else: return _CONFIG_VARS + def get_config_var(name): """Return the value of a single variable using the dictionary returned by 'get_config_vars()'. @@ -576,6 +600,7 @@ def get_config_var(name): """ return get_config_vars().get(name) + def get_platform(): """Return a string that identifies the current platform. @@ -601,7 +626,6 @@ def get_platform(): For other non-POSIX platforms, currently just returns 'sys.platform'. """ - import re if os.name == 'nt': # sniff sys.version for architecture. prefix = " bit (" @@ -617,10 +641,13 @@ def get_platform(): return sys.platform if os.name != "posix" or not hasattr(os, 'uname'): - # XXX what about the architecture? NT is Intel or Alpha, - # Mac OS is M68k or PPC, etc. + # XXX what about the architecture? NT is Intel or Alpha return sys.platform + # Set for cross builds explicitly + if "_PYTHON_HOST_PLATFORM" in os.environ: + return os.environ["_PYTHON_HOST_PLATFORM"] + # Try to distinguish various flavours of Unix osname, host, release, version, machine = os.uname() @@ -651,97 +678,15 @@ def get_platform(): return "%s-%s.%s" % (osname, version, release) elif osname[:6] == "cygwin": osname = "cygwin" - rel_re = re.compile (r'[\d.]+') + rel_re = re.compile(r'[\d.]+') m = rel_re.match(release) if m: release = m.group() elif osname[:6] == "darwin": - # - # For our purposes, we'll assume that the system version from - # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set - # to. This makes the compatibility story a bit more sane because the - # machine is going to compile and link as if it were - # MACOSX_DEPLOYMENT_TARGET. - # - cfgvars = get_config_vars() - macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') - - if 1: - # Always calculate the release of the running machine, - # needed to determine if we can build fat binaries or not. - - macrelease = macver - # Get the system version. Reading this plist is a documented - # way to get the system version (see the documentation for - # the Gestalt Manager) - try: - f = open('/System/Library/CoreServices/SystemVersion.plist') - except IOError: - # We're on a plain darwin box, fall back to the default - # behaviour. - pass - else: - try: - m = re.search( - r'<key>ProductUserVisibleVersion</key>\s*' + - r'<string>(.*?)</string>', f.read()) - if m is not None: - macrelease = '.'.join(m.group(1).split('.')[:2]) - # else: fall back to the default behaviour - finally: - f.close() - - if not macver: - macver = macrelease - - if macver: - release = macver - osname = "macosx" - - if (macrelease + '.') >= '10.4.' and \ - '-arch' in get_config_vars().get('CFLAGS', '').strip(): - # The universal build will build fat binaries, but not on - # systems before 10.4 - # - # Try to detect 4-way universal builds, those have machine-type - # 'universal' instead of 'fat'. - - machine = 'fat' - cflags = get_config_vars().get('CFLAGS') - - archs = re.findall('-arch\s+(\S+)', cflags) - archs = tuple(sorted(set(archs))) - - if len(archs) == 1: - machine = archs[0] - elif archs == ('i386', 'ppc'): - machine = 'fat' - elif archs == ('i386', 'x86_64'): - machine = 'intel' - elif archs == ('i386', 'ppc', 'x86_64'): - machine = 'fat3' - elif archs == ('ppc64', 'x86_64'): - machine = 'fat64' - elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): - machine = 'universal' - else: - raise ValueError( - "Don't know machine value for archs=%r"%(archs,)) - - elif machine == 'i386': - # On OSX the machine type returned by uname is always the - # 32-bit variant, even if the executable architecture is - # the 64-bit variant - if sys.maxsize >= 2**32: - machine = 'x86_64' - - elif machine in ('PowerPC', 'Power_Macintosh'): - # Pick a sane name for the PPC architecture. - # See 'i386' case - if sys.maxsize >= 2**32: - machine = 'ppc64' - else: - machine = 'ppc' + import _osx_support + osname, release, machine = _osx_support.get_platform_osx( + get_config_vars(), + osname, release, machine) return "%s-%s-%s" % (osname, release, machine) @@ -749,21 +694,27 @@ def get_platform(): def get_python_version(): return _PY_VERSION_SHORT + def _print_dict(title, data): for index, (key, value) in enumerate(sorted(data.items())): if index == 0: - print('{0}: '.format(title)) - print('\t{0} = "{1}"'.format(key, value)) + print('%s: ' % (title)) + print('\t%s = "%s"' % (key, value)) + def _main(): """Display all information sysconfig detains.""" - print('Platform: "{0}"'.format(get_platform())) - print('Python version: "{0}"'.format(get_python_version())) - print('Current installation scheme: "{0}"'.format(_get_default_scheme())) - print('') + if '--generate-posix-vars' in sys.argv: + _generate_posix_vars() + return + print('Platform: "%s"' % get_platform()) + print('Python version: "%s"' % get_python_version()) + print('Current installation scheme: "%s"' % _get_default_scheme()) + print() _print_dict('Paths', get_paths()) - print('') + print() _print_dict('Variables', get_config_vars()) + if __name__ == '__main__': _main() |