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