this repo has no description
at trunk 541 lines 20 kB view raw
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)