summaryrefslogtreecommitdiff
path: root/Lib/sysconfig.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/sysconfig.py')
-rw-r--r--Lib/sysconfig.py256
1 files changed, 171 insertions, 85 deletions
diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py
index 5b3a994af9..2a2160cc0d 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:
@@ -351,7 +388,47 @@ def _init_posix(vars):
# -- these paths are relative to the Python source, but when installed
# the scripts are in another directory.
if _PYTHON_BUILD:
- vars['LDSHARED'] = vars['BLDSHARED']
+ vars['BLDSHARED'] = vars['LDSHARED']
+
+ # 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"""
@@ -377,7 +454,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")
@@ -390,8 +466,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)
@@ -399,27 +477,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.
@@ -431,6 +511,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.
@@ -438,17 +519,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 = {}
@@ -460,7 +541,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:
@@ -478,29 +561,22 @@ 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)
+ # 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:
+ # 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
@@ -516,6 +592,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()'.
@@ -524,6 +601,7 @@ def get_config_var(name):
"""
return get_config_vars().get(name)
+
def get_platform():
"""Return a string that identifies the current platform.
@@ -549,7 +627,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 ("
@@ -565,10 +642,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()
@@ -599,7 +679,7 @@ 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()
@@ -615,21 +695,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()