sysconfig.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  1. """Access to Python's configuration information."""
  2. import os
  3. import sys
  4. import threading
  5. from os.path import realpath
  6. __all__ = [
  7. 'get_config_h_filename',
  8. 'get_config_var',
  9. 'get_config_vars',
  10. 'get_makefile_filename',
  11. 'get_path',
  12. 'get_path_names',
  13. 'get_paths',
  14. 'get_platform',
  15. 'get_python_version',
  16. 'get_scheme_names',
  17. 'parse_config_h',
  18. ]
  19. # Keys for get_config_var() that are never converted to Python integers.
  20. _ALWAYS_STR = {
  21. 'MACOSX_DEPLOYMENT_TARGET',
  22. }
  23. _INSTALL_SCHEMES = {
  24. 'posix_prefix': {
  25. 'stdlib': '{installed_base}/{platlibdir}/python{py_version_short}',
  26. 'platstdlib': '{platbase}/{platlibdir}/python{py_version_short}',
  27. 'purelib': '{base}/lib/python{py_version_short}/site-packages',
  28. 'platlib': '{platbase}/{platlibdir}/python{py_version_short}/site-packages',
  29. 'include':
  30. '{installed_base}/include/python{py_version_short}{abiflags}',
  31. 'platinclude':
  32. '{installed_platbase}/include/python{py_version_short}{abiflags}',
  33. 'scripts': '{base}/bin',
  34. 'data': '{base}',
  35. },
  36. 'posix_home': {
  37. 'stdlib': '{installed_base}/lib/python',
  38. 'platstdlib': '{base}/lib/python',
  39. 'purelib': '{base}/lib/python',
  40. 'platlib': '{base}/lib/python',
  41. 'include': '{installed_base}/include/python',
  42. 'platinclude': '{installed_base}/include/python',
  43. 'scripts': '{base}/bin',
  44. 'data': '{base}',
  45. },
  46. 'nt': {
  47. 'stdlib': '{installed_base}/Lib',
  48. 'platstdlib': '{base}/Lib',
  49. 'purelib': '{base}/Lib/site-packages',
  50. 'platlib': '{base}/Lib/site-packages',
  51. 'include': '{installed_base}/Include',
  52. 'platinclude': '{installed_base}/Include',
  53. 'scripts': '{base}/Scripts',
  54. 'data': '{base}',
  55. },
  56. # Downstream distributors can overwrite the default install scheme.
  57. # This is done to support downstream modifications where distributors change
  58. # the installation layout (eg. different site-packages directory).
  59. # So, distributors will change the default scheme to one that correctly
  60. # represents their layout.
  61. # This presents an issue for projects/people that need to bootstrap virtual
  62. # environments, like virtualenv. As distributors might now be customizing
  63. # the default install scheme, there is no guarantee that the information
  64. # returned by sysconfig.get_default_scheme/get_paths is correct for
  65. # a virtual environment, the only guarantee we have is that it is correct
  66. # for the *current* environment. When bootstrapping a virtual environment,
  67. # we need to know its layout, so that we can place the files in the
  68. # correct locations.
  69. # The "*_venv" install scheme is a scheme to bootstrap virtual environments,
  70. # essentially identical to the default posix_prefix/nt schemes.
  71. # Downstream distributors who patch posix_prefix/nt scheme are encouraged to
  72. # leave the following schemes unchanged
  73. 'posix_venv': {
  74. 'stdlib': '{installed_base}/{platlibdir}/python{py_version_short}',
  75. 'platstdlib': '{platbase}/{platlibdir}/python{py_version_short}',
  76. 'purelib': '{base}/lib/python{py_version_short}/site-packages',
  77. 'platlib': '{platbase}/{platlibdir}/python{py_version_short}/site-packages',
  78. 'include':
  79. '{installed_base}/include/python{py_version_short}{abiflags}',
  80. 'platinclude':
  81. '{installed_platbase}/include/python{py_version_short}{abiflags}',
  82. 'scripts': '{base}/bin',
  83. 'data': '{base}',
  84. },
  85. 'nt_venv': {
  86. 'stdlib': '{installed_base}/Lib',
  87. 'platstdlib': '{base}/Lib',
  88. 'purelib': '{base}/Lib/site-packages',
  89. 'platlib': '{base}/Lib/site-packages',
  90. 'include': '{installed_base}/Include',
  91. 'platinclude': '{installed_base}/Include',
  92. 'scripts': '{base}/Scripts',
  93. 'data': '{base}',
  94. },
  95. }
  96. # For the OS-native venv scheme, we essentially provide an alias:
  97. if os.name == 'nt':
  98. _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['nt_venv']
  99. else:
  100. _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['posix_venv']
  101. # NOTE: site.py has copy of this function.
  102. # Sync it when modify this function.
  103. def _getuserbase():
  104. env_base = os.environ.get("PYTHONUSERBASE", None)
  105. if env_base:
  106. return env_base
  107. # Emscripten, VxWorks, and WASI have no home directories
  108. if sys.platform in {"emscripten", "vxworks", "wasi"}:
  109. return None
  110. def joinuser(*args):
  111. return os.path.expanduser(os.path.join(*args))
  112. if os.name == "nt":
  113. base = os.environ.get("APPDATA") or "~"
  114. return joinuser(base, "Python")
  115. if sys.platform == "darwin" and sys._framework:
  116. return joinuser("~", "Library", sys._framework,
  117. f"{sys.version_info[0]}.{sys.version_info[1]}")
  118. return joinuser("~", ".local")
  119. _HAS_USER_BASE = (_getuserbase() is not None)
  120. if _HAS_USER_BASE:
  121. _INSTALL_SCHEMES |= {
  122. # NOTE: When modifying "purelib" scheme, update site._get_path() too.
  123. 'nt_user': {
  124. 'stdlib': '{userbase}/Python{py_version_nodot_plat}',
  125. 'platstdlib': '{userbase}/Python{py_version_nodot_plat}',
  126. 'purelib': '{userbase}/Python{py_version_nodot_plat}/site-packages',
  127. 'platlib': '{userbase}/Python{py_version_nodot_plat}/site-packages',
  128. 'include': '{userbase}/Python{py_version_nodot_plat}/Include',
  129. 'scripts': '{userbase}/Python{py_version_nodot_plat}/Scripts',
  130. 'data': '{userbase}',
  131. },
  132. 'posix_user': {
  133. 'stdlib': '{userbase}/{platlibdir}/python{py_version_short}',
  134. 'platstdlib': '{userbase}/{platlibdir}/python{py_version_short}',
  135. 'purelib': '{userbase}/lib/python{py_version_short}/site-packages',
  136. 'platlib': '{userbase}/lib/python{py_version_short}/site-packages',
  137. 'include': '{userbase}/include/python{py_version_short}',
  138. 'scripts': '{userbase}/bin',
  139. 'data': '{userbase}',
  140. },
  141. 'osx_framework_user': {
  142. 'stdlib': '{userbase}/lib/python',
  143. 'platstdlib': '{userbase}/lib/python',
  144. 'purelib': '{userbase}/lib/python/site-packages',
  145. 'platlib': '{userbase}/lib/python/site-packages',
  146. 'include': '{userbase}/include/python{py_version_short}',
  147. 'scripts': '{userbase}/bin',
  148. 'data': '{userbase}',
  149. },
  150. }
  151. _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
  152. 'scripts', 'data')
  153. _PY_VERSION = sys.version.split()[0]
  154. _PY_VERSION_SHORT = f'{sys.version_info[0]}.{sys.version_info[1]}'
  155. _PY_VERSION_SHORT_NO_DOT = f'{sys.version_info[0]}{sys.version_info[1]}'
  156. _BASE_PREFIX = os.path.normpath(sys.base_prefix)
  157. _BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
  158. # Mutex guarding initialization of _CONFIG_VARS.
  159. _CONFIG_VARS_LOCK = threading.RLock()
  160. _CONFIG_VARS = None
  161. # True iff _CONFIG_VARS has been fully initialized.
  162. _CONFIG_VARS_INITIALIZED = False
  163. _USER_BASE = None
  164. # Regexes needed for parsing Makefile (and similar syntaxes,
  165. # like old-style Setup files).
  166. _variable_rx = r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)"
  167. _findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)"
  168. _findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}"
  169. def _safe_realpath(path):
  170. try:
  171. return realpath(path)
  172. except OSError:
  173. return path
  174. if sys.executable:
  175. _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable))
  176. else:
  177. # sys.executable can be empty if argv[0] has been changed and Python is
  178. # unable to retrieve the real program name
  179. _PROJECT_BASE = _safe_realpath(os.getcwd())
  180. # In a virtual environment, `sys._home` gives us the target directory
  181. # `_PROJECT_BASE` for the executable that created it when the virtual
  182. # python is an actual executable ('venv --copies' or Windows).
  183. _sys_home = getattr(sys, '_home', None)
  184. if _sys_home:
  185. _PROJECT_BASE = _sys_home
  186. if os.name == 'nt':
  187. # In a source build, the executable is in a subdirectory of the root
  188. # that we want (<root>\PCbuild\<platname>).
  189. # `_BASE_PREFIX` is used as the base installation is where the source
  190. # will be. The realpath is needed to prevent mount point confusion
  191. # that can occur with just string comparisons.
  192. if _safe_realpath(_PROJECT_BASE).startswith(
  193. _safe_realpath(f'{_BASE_PREFIX}\\PCbuild')):
  194. _PROJECT_BASE = _BASE_PREFIX
  195. # set for cross builds
  196. if "_PYTHON_PROJECT_BASE" in os.environ:
  197. _PROJECT_BASE = _safe_realpath(os.environ["_PYTHON_PROJECT_BASE"])
  198. def is_python_build(check_home=None):
  199. if check_home is not None:
  200. import warnings
  201. warnings.warn("check_home argument is deprecated and ignored.",
  202. DeprecationWarning, stacklevel=2)
  203. for fn in ("Setup", "Setup.local"):
  204. if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)):
  205. return True
  206. return False
  207. _PYTHON_BUILD = is_python_build()
  208. if _PYTHON_BUILD:
  209. for scheme in ('posix_prefix', 'posix_home'):
  210. # On POSIX-y platforms, Python will:
  211. # - Build from .h files in 'headers' (which is only added to the
  212. # scheme when building CPython)
  213. # - Install .h files to 'include'
  214. scheme = _INSTALL_SCHEMES[scheme]
  215. scheme['headers'] = scheme['include']
  216. scheme['include'] = '{srcdir}/Include'
  217. scheme['platinclude'] = '{projectbase}/.'
  218. del scheme
  219. def _subst_vars(s, local_vars):
  220. try:
  221. return s.format(**local_vars)
  222. except KeyError as var:
  223. try:
  224. return s.format(**os.environ)
  225. except KeyError:
  226. raise AttributeError(f'{var}') from None
  227. def _extend_dict(target_dict, other_dict):
  228. target_keys = target_dict.keys()
  229. for key, value in other_dict.items():
  230. if key in target_keys:
  231. continue
  232. target_dict[key] = value
  233. def _expand_vars(scheme, vars):
  234. res = {}
  235. if vars is None:
  236. vars = {}
  237. _extend_dict(vars, get_config_vars())
  238. if os.name == 'nt':
  239. # On Windows we want to substitute 'lib' for schemes rather
  240. # than the native value (without modifying vars, in case it
  241. # was passed in)
  242. vars = vars | {'platlibdir': 'lib'}
  243. for key, value in _INSTALL_SCHEMES[scheme].items():
  244. if os.name in ('posix', 'nt'):
  245. value = os.path.expanduser(value)
  246. res[key] = os.path.normpath(_subst_vars(value, vars))
  247. return res
  248. def _get_preferred_schemes():
  249. if os.name == 'nt':
  250. return {
  251. 'prefix': 'nt',
  252. 'home': 'posix_home',
  253. 'user': 'nt_user',
  254. }
  255. if sys.platform == 'darwin' and sys._framework:
  256. return {
  257. 'prefix': 'posix_prefix',
  258. 'home': 'posix_home',
  259. 'user': 'osx_framework_user',
  260. }
  261. return {
  262. 'prefix': 'posix_prefix',
  263. 'home': 'posix_home',
  264. 'user': 'posix_user',
  265. }
  266. def get_preferred_scheme(key):
  267. if key == 'prefix' and sys.prefix != sys.base_prefix:
  268. return 'venv'
  269. scheme = _get_preferred_schemes()[key]
  270. if scheme not in _INSTALL_SCHEMES:
  271. raise ValueError(
  272. f"{key!r} returned {scheme!r}, which is not a valid scheme "
  273. f"on this platform"
  274. )
  275. return scheme
  276. def get_default_scheme():
  277. return get_preferred_scheme('prefix')
  278. def _parse_makefile(filename, vars=None, keep_unresolved=True):
  279. """Parse a Makefile-style file.
  280. A dictionary containing name/value pairs is returned. If an
  281. optional dictionary is passed in as the second argument, it is
  282. used instead of a new dictionary.
  283. """
  284. import re
  285. if vars is None:
  286. vars = {}
  287. done = {}
  288. notdone = {}
  289. with open(filename, encoding=sys.getfilesystemencoding(),
  290. errors="surrogateescape") as f:
  291. lines = f.readlines()
  292. for line in lines:
  293. if line.startswith('#') or line.strip() == '':
  294. continue
  295. m = re.match(_variable_rx, line)
  296. if m:
  297. n, v = m.group(1, 2)
  298. v = v.strip()
  299. # `$$' is a literal `$' in make
  300. tmpv = v.replace('$$', '')
  301. if "$" in tmpv:
  302. notdone[n] = v
  303. else:
  304. try:
  305. if n in _ALWAYS_STR:
  306. raise ValueError
  307. v = int(v)
  308. except ValueError:
  309. # insert literal `$'
  310. done[n] = v.replace('$$', '$')
  311. else:
  312. done[n] = v
  313. # do variable interpolation here
  314. variables = list(notdone.keys())
  315. # Variables with a 'PY_' prefix in the makefile. These need to
  316. # be made available without that prefix through sysconfig.
  317. # Special care is needed to ensure that variable expansion works, even
  318. # if the expansion uses the name without a prefix.
  319. renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
  320. while len(variables) > 0:
  321. for name in tuple(variables):
  322. value = notdone[name]
  323. m1 = re.search(_findvar1_rx, value)
  324. m2 = re.search(_findvar2_rx, value)
  325. if m1 and m2:
  326. m = m1 if m1.start() < m2.start() else m2
  327. else:
  328. m = m1 if m1 else m2
  329. if m is not None:
  330. n = m.group(1)
  331. found = True
  332. if n in done:
  333. item = str(done[n])
  334. elif n in notdone:
  335. # get it on a subsequent round
  336. found = False
  337. elif n in os.environ:
  338. # do it like make: fall back to environment
  339. item = os.environ[n]
  340. elif n in renamed_variables:
  341. if (name.startswith('PY_') and
  342. name[3:] in renamed_variables):
  343. item = ""
  344. elif 'PY_' + n in notdone:
  345. found = False
  346. else:
  347. item = str(done['PY_' + n])
  348. else:
  349. done[n] = item = ""
  350. if found:
  351. after = value[m.end():]
  352. value = value[:m.start()] + item + after
  353. if "$" in after:
  354. notdone[name] = value
  355. else:
  356. try:
  357. if name in _ALWAYS_STR:
  358. raise ValueError
  359. value = int(value)
  360. except ValueError:
  361. done[name] = value.strip()
  362. else:
  363. done[name] = value
  364. variables.remove(name)
  365. if name.startswith('PY_') \
  366. and name[3:] in renamed_variables:
  367. name = name[3:]
  368. if name not in done:
  369. done[name] = value
  370. else:
  371. # Adds unresolved variables to the done dict.
  372. # This is disabled when called from distutils.sysconfig
  373. if keep_unresolved:
  374. done[name] = value
  375. # bogus variable reference (e.g. "prefix=$/opt/python");
  376. # just drop it since we can't deal
  377. variables.remove(name)
  378. # strip spurious spaces
  379. for k, v in done.items():
  380. if isinstance(v, str):
  381. done[k] = v.strip()
  382. # save the results in the global dictionary
  383. vars.update(done)
  384. return vars
  385. def get_makefile_filename():
  386. """Return the path of the Makefile."""
  387. if _PYTHON_BUILD:
  388. return os.path.join(_PROJECT_BASE, "Makefile")
  389. if hasattr(sys, 'abiflags'):
  390. config_dir_name = f'config-{_PY_VERSION_SHORT}{sys.abiflags}'
  391. else:
  392. config_dir_name = 'config'
  393. if hasattr(sys.implementation, '_multiarch'):
  394. config_dir_name += f'-{sys.implementation._multiarch}'
  395. return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile')
  396. def _get_sysconfigdata_name():
  397. multiarch = getattr(sys.implementation, '_multiarch', '')
  398. return os.environ.get(
  399. '_PYTHON_SYSCONFIGDATA_NAME',
  400. '_sysconfigdata_arcadia' # f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}',
  401. )
  402. def _generate_posix_vars():
  403. """Generate the Python module containing build-time variables."""
  404. import pprint
  405. vars = {}
  406. # load the installed Makefile:
  407. makefile = get_makefile_filename()
  408. try:
  409. _parse_makefile(makefile, vars)
  410. except OSError as e:
  411. msg = f"invalid Python installation: unable to open {makefile}"
  412. if hasattr(e, "strerror"):
  413. msg = f"{msg} ({e.strerror})"
  414. raise OSError(msg)
  415. # load the installed pyconfig.h:
  416. config_h = get_config_h_filename()
  417. try:
  418. with open(config_h, encoding="utf-8") as f:
  419. parse_config_h(f, vars)
  420. except OSError as e:
  421. msg = f"invalid Python installation: unable to open {config_h}"
  422. if hasattr(e, "strerror"):
  423. msg = f"{msg} ({e.strerror})"
  424. raise OSError(msg)
  425. # On AIX, there are wrong paths to the linker scripts in the Makefile
  426. # -- these paths are relative to the Python source, but when installed
  427. # the scripts are in another directory.
  428. if _PYTHON_BUILD:
  429. vars['BLDSHARED'] = vars['LDSHARED']
  430. # There's a chicken-and-egg situation on OS X with regards to the
  431. # _sysconfigdata module after the changes introduced by #15298:
  432. # get_config_vars() is called by get_platform() as part of the
  433. # `make pybuilddir.txt` target -- which is a precursor to the
  434. # _sysconfigdata.py module being constructed. Unfortunately,
  435. # get_config_vars() eventually calls _init_posix(), which attempts
  436. # to import _sysconfigdata, which we won't have built yet. In order
  437. # for _init_posix() to work, if we're on Darwin, just mock up the
  438. # _sysconfigdata module manually and populate it with the build vars.
  439. # This is more than sufficient for ensuring the subsequent call to
  440. # get_platform() succeeds.
  441. name = _get_sysconfigdata_name()
  442. if 'darwin' in sys.platform:
  443. import types
  444. module = types.ModuleType(name)
  445. module.build_time_vars = vars
  446. sys.modules[name] = module
  447. pybuilddir = f'build/lib.{get_platform()}-{_PY_VERSION_SHORT}'
  448. if hasattr(sys, "gettotalrefcount"):
  449. pybuilddir += '-pydebug'
  450. os.makedirs(pybuilddir, exist_ok=True)
  451. destfile = os.path.join(pybuilddir, name + '.py')
  452. with open(destfile, 'w', encoding='utf8') as f:
  453. f.write('# system configuration generated and used by'
  454. ' the sysconfig module\n')
  455. f.write('build_time_vars = ')
  456. pprint.pprint(vars, stream=f)
  457. # Create file used for sys.path fixup -- see Modules/getpath.c
  458. with open('pybuilddir.txt', 'w', encoding='utf8') as f:
  459. f.write(pybuilddir)
  460. def _init_posix(vars):
  461. """Initialize the module as appropriate for POSIX systems."""
  462. # _sysconfigdata is generated at build time, see _generate_posix_vars()
  463. name = _get_sysconfigdata_name()
  464. _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
  465. build_time_vars = _temp.build_time_vars
  466. vars.update(build_time_vars)
  467. def _init_non_posix(vars):
  468. """Initialize the module as appropriate for NT"""
  469. # set basic install directories
  470. import _imp
  471. vars['LIBDEST'] = get_path('stdlib')
  472. vars['BINLIBDEST'] = get_path('platstdlib')
  473. vars['INCLUDEPY'] = get_path('include')
  474. try:
  475. # GH-99201: _imp.extension_suffixes may be empty when
  476. # HAVE_DYNAMIC_LOADING is not set. In this case, don't set EXT_SUFFIX.
  477. vars['EXT_SUFFIX'] = _imp.extension_suffixes()[0]
  478. except IndexError:
  479. pass
  480. vars['EXE'] = '.exe'
  481. vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT
  482. vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable))
  483. vars['TZPATH'] = ''
  484. #
  485. # public APIs
  486. #
  487. def parse_config_h(fp, vars=None):
  488. """Parse a config.h-style file.
  489. A dictionary containing name/value pairs is returned. If an
  490. optional dictionary is passed in as the second argument, it is
  491. used instead of a new dictionary.
  492. """
  493. if vars is None:
  494. vars = {}
  495. import re
  496. define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
  497. undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
  498. while True:
  499. line = fp.readline()
  500. if not line:
  501. break
  502. m = define_rx.match(line)
  503. if m:
  504. n, v = m.group(1, 2)
  505. try:
  506. if n in _ALWAYS_STR:
  507. raise ValueError
  508. v = int(v)
  509. except ValueError:
  510. pass
  511. vars[n] = v
  512. else:
  513. m = undef_rx.match(line)
  514. if m:
  515. vars[m.group(1)] = 0
  516. return vars
  517. def get_config_h_filename():
  518. """Return the path of pyconfig.h."""
  519. if _PYTHON_BUILD:
  520. if os.name == "nt":
  521. inc_dir = os.path.join(_PROJECT_BASE, "PC")
  522. else:
  523. inc_dir = _PROJECT_BASE
  524. else:
  525. inc_dir = get_path('platinclude')
  526. return os.path.join(inc_dir, 'pyconfig.h')
  527. def get_scheme_names():
  528. """Return a tuple containing the schemes names."""
  529. return tuple(sorted(_INSTALL_SCHEMES))
  530. def get_path_names():
  531. """Return a tuple containing the paths names."""
  532. return _SCHEME_KEYS
  533. def get_paths(scheme=get_default_scheme(), vars=None, expand=True):
  534. """Return a mapping containing an install scheme.
  535. ``scheme`` is the install scheme name. If not provided, it will
  536. return the default scheme for the current platform.
  537. """
  538. if expand:
  539. return _expand_vars(scheme, vars)
  540. else:
  541. return _INSTALL_SCHEMES[scheme]
  542. def get_path(name, scheme=get_default_scheme(), vars=None, expand=True):
  543. """Return a path corresponding to the scheme.
  544. ``scheme`` is the install scheme name.
  545. """
  546. return get_paths(scheme, vars, expand)[name]
  547. def _init_config_vars():
  548. global _CONFIG_VARS
  549. _CONFIG_VARS = {}
  550. # Normalized versions of prefix and exec_prefix are handy to have;
  551. # in fact, these are the standard versions used most places in the
  552. # Distutils.
  553. _PREFIX = os.path.normpath(sys.prefix)
  554. _EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
  555. _CONFIG_VARS['prefix'] = _PREFIX # FIXME: This gets overwriten by _init_posix.
  556. _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX # FIXME: This gets overwriten by _init_posix.
  557. _CONFIG_VARS['py_version'] = _PY_VERSION
  558. _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT
  559. _CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT
  560. _CONFIG_VARS['installed_base'] = _BASE_PREFIX
  561. _CONFIG_VARS['base'] = _PREFIX
  562. _CONFIG_VARS['installed_platbase'] = _BASE_EXEC_PREFIX
  563. _CONFIG_VARS['platbase'] = _EXEC_PREFIX
  564. _CONFIG_VARS['projectbase'] = _PROJECT_BASE
  565. _CONFIG_VARS['platlibdir'] = sys.platlibdir
  566. try:
  567. _CONFIG_VARS['abiflags'] = sys.abiflags
  568. except AttributeError:
  569. # sys.abiflags may not be defined on all platforms.
  570. _CONFIG_VARS['abiflags'] = ''
  571. try:
  572. _CONFIG_VARS['py_version_nodot_plat'] = sys.winver.replace('.', '')
  573. except AttributeError:
  574. _CONFIG_VARS['py_version_nodot_plat'] = ''
  575. if os.name == 'nt':
  576. _init_non_posix(_CONFIG_VARS)
  577. _CONFIG_VARS['VPATH'] = sys._vpath
  578. if os.name == 'posix':
  579. _init_posix(_CONFIG_VARS)
  580. if _HAS_USER_BASE:
  581. # Setting 'userbase' is done below the call to the
  582. # init function to enable using 'get_config_var' in
  583. # the init-function.
  584. _CONFIG_VARS['userbase'] = _getuserbase()
  585. # Always convert srcdir to an absolute path
  586. srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE)
  587. if os.name == 'posix':
  588. if _PYTHON_BUILD:
  589. # If srcdir is a relative path (typically '.' or '..')
  590. # then it should be interpreted relative to the directory
  591. # containing Makefile.
  592. base = os.path.dirname(get_makefile_filename())
  593. srcdir = os.path.join(base, srcdir)
  594. else:
  595. # srcdir is not meaningful since the installation is
  596. # spread about the filesystem. We choose the
  597. # directory containing the Makefile since we know it
  598. # exists.
  599. srcdir = os.path.dirname(get_makefile_filename())
  600. _CONFIG_VARS['srcdir'] = _safe_realpath(srcdir)
  601. # OS X platforms require special customization to handle
  602. # multi-architecture, multi-os-version installers
  603. if sys.platform == 'darwin':
  604. import _osx_support
  605. _osx_support.customize_config_vars(_CONFIG_VARS)
  606. global _CONFIG_VARS_INITIALIZED
  607. _CONFIG_VARS_INITIALIZED = True
  608. def get_config_vars(*args):
  609. """With no arguments, return a dictionary of all configuration
  610. variables relevant for the current platform.
  611. On Unix, this means every variable defined in Python's installed Makefile;
  612. On Windows it's a much smaller set.
  613. With arguments, return a list of values that result from looking up
  614. each argument in the configuration variable dictionary.
  615. """
  616. global _CONFIG_VARS_INITIALIZED
  617. # Avoid claiming the lock once initialization is complete.
  618. if not _CONFIG_VARS_INITIALIZED:
  619. with _CONFIG_VARS_LOCK:
  620. # Test again with the lock held to avoid races. Note that
  621. # we test _CONFIG_VARS here, not _CONFIG_VARS_INITIALIZED,
  622. # to ensure that recursive calls to get_config_vars()
  623. # don't re-enter init_config_vars().
  624. if _CONFIG_VARS is None:
  625. _init_config_vars()
  626. else:
  627. # If the site module initialization happened after _CONFIG_VARS was
  628. # initialized, a virtual environment might have been activated, resulting in
  629. # variables like sys.prefix changing their value, so we need to re-init the
  630. # config vars (see GH-126789).
  631. if _CONFIG_VARS['base'] != os.path.normpath(sys.prefix):
  632. with _CONFIG_VARS_LOCK:
  633. _CONFIG_VARS_INITIALIZED = False
  634. _init_config_vars()
  635. if args:
  636. vals = []
  637. for name in args:
  638. vals.append(_CONFIG_VARS.get(name))
  639. return vals
  640. else:
  641. return _CONFIG_VARS
  642. def get_config_var(name):
  643. """Return the value of a single variable using the dictionary returned by
  644. 'get_config_vars()'.
  645. Equivalent to get_config_vars().get(name)
  646. """
  647. return get_config_vars().get(name)
  648. def get_platform():
  649. """Return a string that identifies the current platform.
  650. This is used mainly to distinguish platform-specific build directories and
  651. platform-specific built distributions. Typically includes the OS name and
  652. version and the architecture (as supplied by 'os.uname()'), although the
  653. exact information included depends on the OS; on Linux, the kernel version
  654. isn't particularly important.
  655. Examples of returned values:
  656. linux-i586
  657. linux-alpha (?)
  658. solaris-2.6-sun4u
  659. Windows will return one of:
  660. win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
  661. win32 (all others - specifically, sys.platform is returned)
  662. For other non-POSIX platforms, currently just returns 'sys.platform'.
  663. """
  664. if os.name == 'nt':
  665. if 'amd64' in sys.version.lower():
  666. return 'win-amd64'
  667. if '(arm)' in sys.version.lower():
  668. return 'win-arm32'
  669. if '(arm64)' in sys.version.lower():
  670. return 'win-arm64'
  671. return sys.platform
  672. if os.name != "posix" or not hasattr(os, 'uname'):
  673. # XXX what about the architecture? NT is Intel or Alpha
  674. return sys.platform
  675. # Set for cross builds explicitly
  676. if "_PYTHON_HOST_PLATFORM" in os.environ:
  677. return os.environ["_PYTHON_HOST_PLATFORM"]
  678. # Try to distinguish various flavours of Unix
  679. osname, host, release, version, machine = os.uname()
  680. # Convert the OS name to lowercase, remove '/' characters, and translate
  681. # spaces (for "Power Macintosh")
  682. osname = osname.lower().replace('/', '')
  683. machine = machine.replace(' ', '_')
  684. machine = machine.replace('/', '-')
  685. if osname[:5] == "linux":
  686. # At least on Linux/Intel, 'machine' is the processor --
  687. # i386, etc.
  688. # XXX what about Alpha, SPARC, etc?
  689. return f"{osname}-{machine}"
  690. elif osname[:5] == "sunos":
  691. if release[0] >= "5": # SunOS 5 == Solaris 2
  692. osname = "solaris"
  693. release = f"{int(release[0]) - 3}.{release[2:]}"
  694. # We can't use "platform.architecture()[0]" because a
  695. # bootstrap problem. We use a dict to get an error
  696. # if some suspicious happens.
  697. bitness = {2147483647:"32bit", 9223372036854775807:"64bit"}
  698. machine += f".{bitness[sys.maxsize]}"
  699. # fall through to standard osname-release-machine representation
  700. elif osname[:3] == "aix":
  701. from _aix_support import aix_platform
  702. return aix_platform()
  703. elif osname[:6] == "cygwin":
  704. osname = "cygwin"
  705. import re
  706. rel_re = re.compile(r'[\d.]+')
  707. m = rel_re.match(release)
  708. if m:
  709. release = m.group()
  710. elif osname[:6] == "darwin":
  711. import _osx_support
  712. osname, release, machine = _osx_support.get_platform_osx(
  713. get_config_vars(),
  714. osname, release, machine)
  715. return f"{osname}-{release}-{machine}"
  716. def get_python_version():
  717. return _PY_VERSION_SHORT
  718. def expand_makefile_vars(s, vars):
  719. """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
  720. 'string' according to 'vars' (a dictionary mapping variable names to
  721. values). Variables not present in 'vars' are silently expanded to the
  722. empty string. The variable values in 'vars' should not contain further
  723. variable expansions; if 'vars' is the output of 'parse_makefile()',
  724. you're fine. Returns a variable-expanded version of 's'.
  725. """
  726. import re
  727. # This algorithm does multiple expansion, so if vars['foo'] contains
  728. # "${bar}", it will expand ${foo} to ${bar}, and then expand
  729. # ${bar}... and so forth. This is fine as long as 'vars' comes from
  730. # 'parse_makefile()', which takes care of such expansions eagerly,
  731. # according to make's variable expansion semantics.
  732. while True:
  733. m = re.search(_findvar1_rx, s) or re.search(_findvar2_rx, s)
  734. if m:
  735. (beg, end) = m.span()
  736. s = s[0:beg] + vars.get(m.group(1)) + s[end:]
  737. else:
  738. break
  739. return s
  740. def _print_dict(title, data):
  741. for index, (key, value) in enumerate(sorted(data.items())):
  742. if index == 0:
  743. print(f'{title}: ')
  744. print(f'\t{key} = "{value}"')
  745. def _main():
  746. """Display all information sysconfig detains."""
  747. if '--generate-posix-vars' in sys.argv:
  748. _generate_posix_vars()
  749. return
  750. print(f'Platform: "{get_platform()}"')
  751. print(f'Python version: "{get_python_version()}"')
  752. print(f'Current installation scheme: "{get_default_scheme()}"')
  753. print()
  754. _print_dict('Paths', get_paths())
  755. print()
  756. _print_dict('Variables', get_config_vars())
  757. if __name__ == '__main__':
  758. _main()