this repo has no description
1#!/usr/bin/env python3
2# Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
3# This is a copy of code from the cpython library. Do not unnecessarily deviate
4# from the upstream style so updating to newer cpython versions stays easy.
5# flake8: noqa
6# fmt: off
7"""Provide access to Python's configuration information. The specific
8configuration variables available depend heavily on the platform and
9configuration. The values may be retrieved using
10get_config_var(name), and the list of variables is available via
11get_config_vars().keys(). Additional convenience functions are also
12available.
13
14Written by: Fred L. Drake, Jr.
15Email: <fdrake@acm.org>
16"""
17
18import _imp
19import os
20import re
21import sys
22import sysconfig as _sysconfig
23
24from .errors import DistutilsPlatformError
25from .util import get_platform, get_host_platform
26
27# These are needed in a couple of spots, so just compute them once.
28PREFIX = os.path.normpath(sys.prefix)
29EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
30BASE_PREFIX = os.path.normpath(sys.base_prefix)
31BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
32
33# Path to the base directory of the project. On Windows the binary may
34# live in project/PCbuild/win32 or project/PCbuild/amd64.
35# set for cross builds
36if "_PYTHON_PROJECT_BASE" in os.environ:
37 project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"])
38else:
39 if sys.executable:
40 project_base = os.path.dirname(os.path.abspath(sys.executable))
41 else:
42 # sys.executable can be empty if argv[0] has been changed and Python is
43 # unable to retrieve the real program name
44 project_base = os.getcwd()
45
46
47# python_build: (Boolean) if true, we're either building Python or
48# building an extension with an un-installed Python, so we use
49# different (hard-wired) directories.
50def _is_python_source_dir(d):
51 for fn in ("Setup", "Setup.local"):
52 if os.path.isfile(os.path.join(d, "Modules", fn)):
53 return True
54 return False
55
56_sys_home = getattr(sys, '_home', None)
57
58if os.name == 'nt':
59 def _fix_pcbuild(d):
60 if d and os.path.normcase(d).startswith(
61 os.path.normcase(os.path.join(PREFIX, "PCbuild"))):
62 return PREFIX
63 return d
64 project_base = _fix_pcbuild(project_base)
65 _sys_home = _fix_pcbuild(_sys_home)
66
67def _python_build():
68 if _sys_home:
69 return _is_python_source_dir(_sys_home)
70 return _is_python_source_dir(project_base)
71
72python_build = _python_build()
73
74
75# Calculate the build qualifier flags if they are defined. Adding the flags
76# to the include and lib directories only makes sense for an installation, not
77# an in-source build.
78build_flags = ''
79try:
80 if not python_build:
81 build_flags = sys.abiflags
82except AttributeError:
83 # It's not a configure-based build, so the sys module doesn't have
84 # this attribute, which is fine.
85 pass
86
87def get_python_version():
88 """Return a string containing the major and minor Python version,
89 leaving off the patchlevel. Sample return values could be '1.5'
90 or '2.2'.
91 """
92 return '%d.%d' % sys.version_info[:2]
93
94
95def get_python_inc(plat_specific=0, prefix=None):
96 """Return the directory containing installed Python header files.
97
98 If 'plat_specific' is false (the default), this is the path to the
99 non-platform-specific header files, i.e. Python.h and so on;
100 otherwise, this is the path to platform-specific header files
101 (namely pyconfig.h).
102
103 If 'prefix' is supplied, use it instead of sys.base_prefix or
104 sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
105 """
106 if os.name != "nt":
107 vars = None
108 if prefix is not None:
109 vars = {
110 "installed_base": prefix,
111 "installed_platbase": prefix,
112 }
113 if plat_specific:
114 return _sysconfig.get_path("platinclude", vars=vars)
115 return _sysconfig.get_path("include", vars=vars)
116
117 if prefix is None:
118 prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
119 if python_build:
120 # Include both the include and PC dir to ensure we can find
121 # pyconfig.h
122 return (os.path.join(prefix, "include") + os.path.pathsep +
123 os.path.join(prefix, "PC"))
124 return os.path.join(prefix, "include")
125
126
127def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
128 """Return the directory containing the Python library (standard or
129 site additions).
130
131 If 'plat_specific' is true, return the directory containing
132 platform-specific modules, i.e. any module from a non-pure-Python
133 module distribution; otherwise, return the platform-shared library
134 directory. If 'standard_lib' is true, return the directory
135 containing standard Python library modules; otherwise, return the
136 directory for site-specific modules.
137
138 If 'prefix' is supplied, use it instead of sys.base_prefix or
139 sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
140 """
141 vars = None
142 if prefix is not None:
143 vars = {
144 "base": prefix,
145 "installed_base": prefix,
146 "platbase": prefix,
147 "userbase": prefix,
148 }
149 if standard_lib:
150 if plat_specific:
151 return _sysconfig.get_path("platstdlib", vars=vars)
152 return _sysconfig.get_path("stdlib", vars=vars)
153 else:
154 if plat_specific:
155 return _sysconfig.get_path("platlib", vars=vars)
156 return _sysconfig.get_path("purelib", vars=vars)
157
158
159def customize_compiler(compiler):
160 """Do any platform-specific customization of a CCompiler instance.
161
162 Mainly needed on Unix, so we can plug in the information that
163 varies across Unices and is stored in Python's Makefile.
164 """
165 if compiler.compiler_type == "unix":
166 if sys.platform == "darwin":
167 # Perform first-time customization of compiler-related
168 # config vars on OS X now that we know we need a compiler.
169 # This is primarily to support Pythons from binary
170 # installers. The kind and paths to build tools on
171 # the user system may vary significantly from the system
172 # that Python itself was built on. Also the user OS
173 # version and build tools may not support the same set
174 # of CPU architectures for universal builds.
175 global _config_vars
176 # Use get_config_var() to ensure _config_vars is initialized.
177 if not get_config_var('CUSTOMIZED_OSX_COMPILER'):
178 import _osx_support
179 _osx_support.customize_compiler(_config_vars)
180 _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
181
182 (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \
183 get_config_vars('CC', 'CXX', 'CFLAGS',
184 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS')
185
186 if 'CC' in os.environ:
187 newcc = os.environ['CC']
188 if (sys.platform == 'darwin'
189 and 'LDSHARED' not in os.environ
190 and ldshared.startswith(cc)):
191 # On OS X, if CC is overridden, use that as the default
192 # command for LDSHARED as well
193 ldshared = newcc + ldshared[len(cc):]
194 cc = newcc
195 if 'CXX' in os.environ:
196 cxx = os.environ['CXX']
197 if 'LDSHARED' in os.environ:
198 ldshared = os.environ['LDSHARED']
199 if 'CPP' in os.environ:
200 cpp = os.environ['CPP']
201 else:
202 cpp = cc + " -E" # not always
203 if 'LDFLAGS' in os.environ:
204 ldshared = ldshared + ' ' + os.environ['LDFLAGS']
205 if 'CFLAGS' in os.environ:
206 cflags = cflags + ' ' + os.environ['CFLAGS']
207 ldshared = ldshared + ' ' + os.environ['CFLAGS']
208 if 'CPPFLAGS' in os.environ:
209 cpp = cpp + ' ' + os.environ['CPPFLAGS']
210 cflags = cflags + ' ' + os.environ['CPPFLAGS']
211 ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
212 if 'AR' in os.environ:
213 ar = os.environ['AR']
214 if 'ARFLAGS' in os.environ:
215 archiver = ar + ' ' + os.environ['ARFLAGS']
216 else:
217 archiver = ar + ' ' + ar_flags
218
219 cc_cmd = cc + ' ' + cflags
220 compiler.set_executables(
221 preprocessor=cpp,
222 compiler=cc_cmd,
223 compiler_so=cc_cmd + ' ' + ccshared,
224 compiler_cxx=cxx,
225 linker_so=ldshared,
226 linker_exe=cc,
227 archiver=archiver)
228
229 compiler.shared_lib_extension = shlib_suffix
230
231
232def get_config_h_filename():
233 """Return full pathname of installed pyconfig.h file."""
234 if python_build:
235 if os.name == "nt":
236 inc_dir = os.path.join(_sys_home or project_base, "PC")
237 else:
238 inc_dir = _sys_home or project_base
239 else:
240 inc_dir = get_python_inc(plat_specific=1)
241
242 return os.path.join(inc_dir, 'pyconfig.h')
243
244
245def get_makefile_filename():
246 """Return full pathname of installed Makefile from the Python build."""
247 if python_build:
248 return os.path.join(_sys_home or project_base, "Makefile")
249 lib_dir = get_python_lib(plat_specific=0, standard_lib=1)
250 config_file = 'config-{}{}'.format(get_python_version(), build_flags)
251 if hasattr(sys.implementation, '_multiarch'):
252 config_file += '-%s' % sys.implementation._multiarch
253 return os.path.join(lib_dir, config_file, 'Makefile')
254
255
256def parse_config_h(fp, g=None):
257 """Parse a config.h-style file.
258
259 A dictionary containing name/value pairs is returned. If an
260 optional dictionary is passed in as the second argument, it is
261 used instead of a new dictionary.
262 """
263 if g is None:
264 g = {}
265 define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
266 undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
267 #
268 while True:
269 line = fp.readline()
270 if not line:
271 break
272 m = define_rx.match(line)
273 if m:
274 n, v = m.group(1, 2)
275 try: v = int(v)
276 except ValueError: pass
277 g[n] = v
278 else:
279 m = undef_rx.match(line)
280 if m:
281 g[m.group(1)] = 0
282 return g
283
284
285# Regexes needed for parsing Makefile (and similar syntaxes,
286# like old-style Setup files).
287_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
288_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
289_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
290
291def parse_makefile(fn, g=None):
292 """Parse a Makefile-style file.
293
294 A dictionary containing name/value pairs is returned. If an
295 optional dictionary is passed in as the second argument, it is
296 used instead of a new dictionary.
297 """
298 from distutils.text_file import TextFile
299 fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape")
300
301 if g is None:
302 g = {}
303 done = {}
304 notdone = {}
305
306 while True:
307 line = fp.readline()
308 if line is None: # eof
309 break
310 m = _variable_rx.match(line)
311 if m:
312 n, v = m.group(1, 2)
313 v = v.strip()
314 # `$$' is a literal `$' in make
315 tmpv = v.replace('$$', '')
316
317 if "$" in tmpv:
318 notdone[n] = v
319 else:
320 try:
321 v = int(v)
322 except ValueError:
323 # insert literal `$'
324 done[n] = v.replace('$$', '$')
325 else:
326 done[n] = v
327
328 # Variables with a 'PY_' prefix in the makefile. These need to
329 # be made available without that prefix through sysconfig.
330 # Special care is needed to ensure that variable expansion works, even
331 # if the expansion uses the name without a prefix.
332 renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
333
334 # do variable interpolation here
335 while notdone:
336 for name in list(notdone):
337 value = notdone[name]
338 m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
339 if m:
340 n = m.group(1)
341 found = True
342 if n in done:
343 item = str(done[n])
344 elif n in notdone:
345 # get it on a subsequent round
346 found = False
347 elif n in os.environ:
348 # do it like make: fall back to environment
349 item = os.environ[n]
350
351 elif n in renamed_variables:
352 if name.startswith('PY_') and name[3:] in renamed_variables:
353 item = ""
354
355 elif 'PY_' + n in notdone:
356 found = False
357
358 else:
359 item = str(done['PY_' + n])
360 else:
361 done[n] = item = ""
362 if found:
363 after = value[m.end():]
364 value = value[:m.start()] + item + after
365 if "$" in after:
366 notdone[name] = value
367 else:
368 try: value = int(value)
369 except ValueError:
370 done[name] = value.strip()
371 else:
372 done[name] = value
373 del notdone[name]
374
375 if name.startswith('PY_') \
376 and name[3:] in renamed_variables:
377
378 name = name[3:]
379 if name not in done:
380 done[name] = value
381 else:
382 # bogus variable reference; just drop it since we can't deal
383 del notdone[name]
384
385 fp.close()
386
387 # strip spurious spaces
388 for k, v in done.items():
389 if isinstance(v, str):
390 done[k] = v.strip()
391
392 # save the results in the global dictionary
393 g.update(done)
394 return g
395
396
397def expand_makefile_vars(s, vars):
398 """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
399 'string' according to 'vars' (a dictionary mapping variable names to
400 values). Variables not present in 'vars' are silently expanded to the
401 empty string. The variable values in 'vars' should not contain further
402 variable expansions; if 'vars' is the output of 'parse_makefile()',
403 you're fine. Returns a variable-expanded version of 's'.
404 """
405
406 # This algorithm does multiple expansion, so if vars['foo'] contains
407 # "${bar}", it will expand ${foo} to ${bar}, and then expand
408 # ${bar}... and so forth. This is fine as long as 'vars' comes from
409 # 'parse_makefile()', which takes care of such expansions eagerly,
410 # according to make's variable expansion semantics.
411
412 while True:
413 m = _findvar1_rx.search(s) or _findvar2_rx.search(s)
414 if m:
415 (beg, end) = m.span()
416 s = s[0:beg] + vars.get(m.group(1)) + s[end:]
417 else:
418 break
419 return s
420
421
422_config_vars = None
423
424def _init_posix():
425 """Initialize the module as appropriate for POSIX systems."""
426 # _sysconfigdata is generated at build time, see the sysconfig module
427 name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME',
428 '_sysconfigdata_{abi}_{platform}_{multiarch}'.format(
429 abi=sys.abiflags,
430 platform=sys.platform,
431 multiarch=getattr(sys.implementation, '_multiarch', ''),
432 ))
433 _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
434 build_time_vars = _temp.build_time_vars
435 global _config_vars
436 _config_vars = {}
437 _config_vars.update(build_time_vars)
438
439
440def _init_nt():
441 """Initialize the module as appropriate for NT"""
442 g = {}
443 # set basic install directories
444 g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
445 g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
446
447 # XXX hmmm.. a normal install puts include files here
448 g['INCLUDEPY'] = get_python_inc(plat_specific=0)
449
450 g['EXT_SUFFIX'] = _imp.extension_suffixes()[0]
451 g['EXE'] = ".exe"
452 g['VERSION'] = get_python_version().replace(".", "")
453 g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable))
454
455 global _config_vars
456 _config_vars = g
457
458
459def get_config_vars(*args):
460 """With no arguments, return a dictionary of all configuration
461 variables relevant for the current platform. Generally this includes
462 everything needed to build extensions and install both pure modules and
463 extensions. On Unix, this means every variable defined in Python's
464 installed Makefile; on Windows it's a much smaller set.
465
466 With arguments, return a list of values that result from looking up
467 each argument in the configuration variable dictionary.
468 """
469 global _config_vars
470 if _config_vars is None:
471 func = globals().get("_init_" + os.name)
472 if func:
473 func()
474 else:
475 _config_vars = {}
476
477 # Normalized versions of prefix and exec_prefix are handy to have;
478 # in fact, these are the standard versions used most places in the
479 # Distutils.
480 _config_vars['prefix'] = PREFIX
481 _config_vars['exec_prefix'] = EXEC_PREFIX
482
483 # For backward compatibility, see issue19555
484 SO = _config_vars.get('EXT_SUFFIX')
485 if SO is not None:
486 _config_vars['SO'] = SO
487
488 # Always convert srcdir to an absolute path
489 srcdir = _config_vars.get('srcdir', project_base)
490 if os.name == 'posix':
491 if python_build:
492 # If srcdir is a relative path (typically '.' or '..')
493 # then it should be interpreted relative to the directory
494 # containing Makefile.
495 base = os.path.dirname(get_makefile_filename())
496 srcdir = os.path.join(base, srcdir)
497 else:
498 # srcdir is not meaningful since the installation is
499 # spread about the filesystem. We choose the
500 # directory containing the Makefile since we know it
501 # exists.
502 srcdir = os.path.dirname(get_makefile_filename())
503 _config_vars['srcdir'] = os.path.abspath(os.path.normpath(srcdir))
504
505 # Convert srcdir into an absolute path if it appears necessary.
506 # Normally it is relative to the build directory. However, during
507 # testing, for example, we might be running a non-installed python
508 # from a different directory.
509 if python_build and os.name == "posix":
510 base = project_base
511 if (not os.path.isabs(_config_vars['srcdir']) and
512 base != os.getcwd()):
513 # srcdir is relative and we are not in the same directory
514 # as the executable. Assume executable is in the build
515 # directory and make srcdir absolute.
516 srcdir = os.path.join(base, _config_vars['srcdir'])
517 _config_vars['srcdir'] = os.path.normpath(srcdir)
518
519 # OS X platforms require special customization to handle
520 # multi-architecture, multi-os-version installers
521 if sys.platform == 'darwin':
522 import _osx_support
523 _osx_support.customize_config_vars(_config_vars)
524
525 if args:
526 vals = []
527 for name in args:
528 vals.append(_config_vars.get(name))
529 return vals
530 else:
531 return _config_vars
532
533def get_config_var(name):
534 """Return the value of a single variable using the dictionary
535 returned by 'get_config_vars()'. Equivalent to
536 get_config_vars().get(name)
537 """
538 if name == 'SO':
539 import warnings
540 warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2)
541 return get_config_vars().get(name)