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