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 16import sysconfig 17 18from .errors import DistutilsPlatformError 19 20IS_PYPY = '__pypy__' in sys.builtin_module_names 21 22# These are needed in a couple of spots, so just compute them once. 23PREFIX = os.path.normpath(sys.prefix) 24EXEC_PREFIX = os.path.normpath(sys.exec_prefix) 25BASE_PREFIX = os.path.normpath(sys.base_prefix) 26BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) 27 28# Path to the base directory of the project. On Windows the binary may 29# live in project/PCbuild/win32 or project/PCbuild/amd64. 30# set for cross builds 31if "_PYTHON_PROJECT_BASE" in os.environ: 32 project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) 33else: 34 if sys.executable: 35 project_base = os.path.dirname(os.path.abspath(sys.executable)) 36 else: 37 # sys.executable can be empty if argv[0] has been changed and Python is 38 # unable to retrieve the real program name 39 project_base = os.getcwd() 40 41 42# python_build: (Boolean) if true, we're either building Python or 43# building an extension with an un-installed Python, so we use 44# different (hard-wired) directories. 45def _is_python_source_dir(d): 46 for fn in ("Setup", "Setup.local"): 47 if os.path.isfile(os.path.join(d, "Modules", fn)): 48 return True 49 return False 50 51_sys_home = getattr(sys, '_home', None) 52 53if os.name == 'nt': 54 def _fix_pcbuild(d): 55 if d and os.path.normcase(d).startswith( 56 os.path.normcase(os.path.join(PREFIX, "PCbuild"))): 57 return PREFIX 58 return d 59 project_base = _fix_pcbuild(project_base) 60 _sys_home = _fix_pcbuild(_sys_home) 61 62def _python_build(): 63 if _sys_home: 64 return _is_python_source_dir(_sys_home) 65 return _is_python_source_dir(project_base) 66 67python_build = _python_build() 68 69 70# Calculate the build qualifier flags if they are defined. Adding the flags 71# to the include and lib directories only makes sense for an installation, not 72# an in-source build. 73build_flags = '' 74try: 75 if not python_build: 76 build_flags = sys.abiflags 77except AttributeError: 78 # It's not a configure-based build, so the sys module doesn't have 79 # this attribute, which is fine. 80 pass 81 82def get_python_version(): 83 """Return a string containing the major and minor Python version, 84 leaving off the patchlevel. Sample return values could be '1.5' 85 or '2.2'. 86 """ 87 return '%d.%d' % sys.version_info[:2] 88 89 90def get_python_inc(plat_specific=0, prefix=None): 91 """Return the directory containing installed Python header files. 92 93 If 'plat_specific' is false (the default), this is the path to the 94 non-platform-specific header files, i.e. Python.h and so on; 95 otherwise, this is the path to platform-specific header files 96 (namely pyconfig.h). 97 98 If 'prefix' is supplied, use it instead of sys.base_prefix or 99 sys.base_exec_prefix -- i.e., ignore 'plat_specific'. 100 """ 101 if prefix is None: 102 prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX 103 if os.name == "posix": 104 if IS_PYPY and sys.version_info < (3, 8): 105 return os.path.join(prefix, 'include') 106 if python_build: 107 # Assume the executable is in the build directory. The 108 # pyconfig.h file should be in the same directory. Since 109 # the build directory may not be the source directory, we 110 # must use "srcdir" from the makefile to find the "Include" 111 # directory. 112 if plat_specific: 113 return _sys_home or project_base 114 else: 115 incdir = os.path.join(get_config_var('srcdir'), 'Include') 116 return os.path.normpath(incdir) 117 implementation = 'pypy' if IS_PYPY else 'python' 118 python_dir = implementation + get_python_version() + build_flags 119 return os.path.join(prefix, "include", python_dir) 120 elif os.name == "nt": 121 if python_build: 122 # Include both the include and PC dir to ensure we can find 123 # pyconfig.h 124 return (os.path.join(prefix, "include") + os.path.pathsep + 125 os.path.join(prefix, "PC")) 126 return os.path.join(prefix, "include") 127 else: 128 raise DistutilsPlatformError( 129 "I don't know where Python installs its C header files " 130 "on platform '%s'" % os.name) 131 132 133# allow this behavior to be monkey-patched. Ref pypa/distutils#2. 134def _posix_lib(standard_lib, libpython, early_prefix, prefix): 135 if standard_lib: 136 return libpython 137 else: 138 return os.path.join(libpython, "site-packages") 139 140 141def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): 142 """Return the directory containing the Python library (standard or 143 site additions). 144 145 If 'plat_specific' is true, return the directory containing 146 platform-specific modules, i.e. any module from a non-pure-Python 147 module distribution; otherwise, return the platform-shared library 148 directory. If 'standard_lib' is true, return the directory 149 containing standard Python library modules; otherwise, return the 150 directory for site-specific modules. 151 152 If 'prefix' is supplied, use it instead of sys.base_prefix or 153 sys.base_exec_prefix -- i.e., ignore 'plat_specific'. 154 """ 155 156 if IS_PYPY and sys.version_info < (3, 8): 157 # PyPy-specific schema 158 if prefix is None: 159 prefix = PREFIX 160 if standard_lib: 161 return os.path.join(prefix, "lib-python", sys.version[0]) 162 return os.path.join(prefix, 'site-packages') 163 164 early_prefix = prefix 165 166 if prefix is None: 167 if standard_lib: 168 prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX 169 else: 170 prefix = plat_specific and EXEC_PREFIX or PREFIX 171 172 if os.name == "posix": 173 if plat_specific or standard_lib: 174 # Platform-specific modules (any module from a non-pure-Python 175 # module distribution) or standard Python library modules. 176 libdir = getattr(sys, "platlibdir", "lib") 177 else: 178 # Pure Python 179 libdir = "lib" 180 implementation = 'pypy' if IS_PYPY else 'python' 181 libpython = os.path.join(prefix, libdir, 182 implementation + get_python_version()) 183 return _posix_lib(standard_lib, libpython, early_prefix, prefix) 184 elif os.name == "nt": 185 if standard_lib: 186 return os.path.join(prefix, "Lib") 187 else: 188 return os.path.join(prefix, "Lib", "site-packages") 189 else: 190 raise DistutilsPlatformError( 191 "I don't know where Python installs its library " 192 "on platform '%s'" % os.name) 193 194 195 196def customize_compiler(compiler): 197 """Do any platform-specific customization of a CCompiler instance. 198 199 Mainly needed on Unix, so we can plug in the information that 200 varies across Unices and is stored in Python's Makefile. 201 """ 202 if compiler.compiler_type == "unix": 203 if sys.platform == "darwin": 204 # Perform first-time customization of compiler-related 205 # config vars on OS X now that we know we need a compiler. 206 # This is primarily to support Pythons from binary 207 # installers. The kind and paths to build tools on 208 # the user system may vary significantly from the system 209 # that Python itself was built on. Also the user OS 210 # version and build tools may not support the same set 211 # of CPU architectures for universal builds. 212 global _config_vars 213 # Use get_config_var() to ensure _config_vars is initialized. 214 if not get_config_var('CUSTOMIZED_OSX_COMPILER'): 215 import _osx_support 216 _osx_support.customize_compiler(_config_vars) 217 _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' 218 219 (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \ 220 get_config_vars('CC', 'CXX', 'CFLAGS', 221 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS') 222 223 if 'CC' in os.environ: 224 newcc = os.environ['CC'] 225 if('LDSHARED' not in os.environ 226 and ldshared.startswith(cc)): 227 # If CC is overridden, use that as the default 228 # command for LDSHARED as well 229 ldshared = newcc + ldshared[len(cc):] 230 cc = newcc 231 if 'CXX' in os.environ: 232 cxx = os.environ['CXX'] 233 if 'LDSHARED' in os.environ: 234 ldshared = os.environ['LDSHARED'] 235 if 'CPP' in os.environ: 236 cpp = os.environ['CPP'] 237 else: 238 cpp = cc + " -E" # not always 239 if 'LDFLAGS' in os.environ: 240 ldshared = ldshared + ' ' + os.environ['LDFLAGS'] 241 if 'CFLAGS' in os.environ: 242 cflags = cflags + ' ' + os.environ['CFLAGS'] 243 ldshared = ldshared + ' ' + os.environ['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 if 'AR' in os.environ: 249 ar = os.environ['AR'] 250 if 'ARFLAGS' in os.environ: 251 archiver = ar + ' ' + os.environ['ARFLAGS'] 252 else: 253 archiver = ar + ' ' + ar_flags 254 255 cc_cmd = cc + ' ' + cflags 256 compiler.set_executables( 257 preprocessor=cpp, 258 compiler=cc_cmd, 259 compiler_so=cc_cmd + ' ' + ccshared, 260 compiler_cxx=cxx, 261 linker_so=ldshared, 262 linker_exe=cc, 263 archiver=archiver) 264 265 if 'RANLIB' in os.environ and compiler.executables.get('ranlib', None): 266 compiler.set_executables(ranlib=os.environ['RANLIB']) 267 268 compiler.shared_lib_extension = shlib_suffix 269 270 271def get_config_h_filename(): 272 """Return full pathname of installed pyconfig.h file.""" 273 if python_build: 274 if os.name == "nt": 275 inc_dir = os.path.join(_sys_home or project_base, "PC") 276 else: 277 inc_dir = _sys_home or project_base 278 return os.path.join(inc_dir, 'pyconfig.h') 279 else: 280 return sysconfig.get_config_h_filename() 281 282 283 284def get_makefile_filename(): 285 """Return full pathname of installed Makefile from the Python build.""" 286 return sysconfig.get_makefile_filename() 287 288 289def parse_config_h(fp, g=None): 290 """Parse a config.h-style file. 291 292 A dictionary containing name/value pairs is returned. If an 293 optional dictionary is passed in as the second argument, it is 294 used instead of a new dictionary. 295 """ 296 return sysconfig.parse_config_h(fp, vars=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 438 439def get_config_vars(*args): 440 """With no arguments, return a dictionary of all configuration 441 variables relevant for the current platform. Generally this includes 442 everything needed to build extensions and install both pure modules and 443 extensions. On Unix, this means every variable defined in Python's 444 installed Makefile; on Windows it's a much smaller set. 445 446 With arguments, return a list of values that result from looking up 447 each argument in the configuration variable dictionary. 448 """ 449 global _config_vars 450 if _config_vars is None: 451 _config_vars = sysconfig.get_config_vars().copy() 452 453 if args: 454 vals = [] 455 for name in args: 456 vals.append(_config_vars.get(name)) 457 return vals 458 else: 459 return _config_vars 460 461def get_config_var(name): 462 """Return the value of a single variable using the dictionary 463 returned by 'get_config_vars()'. Equivalent to 464 get_config_vars().get(name) 465 """ 466 if name == 'SO': 467 import warnings 468 warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2) 469 return get_config_vars().get(name) 470