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