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 12__revision__ = "$Id$" 13 14import os 15import re 16import string 17import sys 18 19from distutils.errors import DistutilsPlatformError 20 21# These are needed in a couple of spots, so just compute them once. 22PREFIX = os.path.normpath(sys.prefix) 23EXEC_PREFIX = os.path.normpath(sys.exec_prefix) 24 25# Path to the base directory of the project. On Windows the binary may 26# live in project/PCBuild9. If we're dealing with an x64 Windows build, 27# it'll live in project/PCbuild/amd64. 28project_base = os.path.dirname(os.path.abspath(sys.executable)) 29if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): 30 project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) 31# PC/VS7.1 32if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): 33 project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, 34 os.path.pardir)) 35# PC/AMD64 36if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower(): 37 project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, 38 os.path.pardir)) 39 40# set for cross builds 41if "_PYTHON_PROJECT_BASE" in os.environ: 42 # this is the build directory, at least for posix 43 project_base = os.path.normpath(os.environ["_PYTHON_PROJECT_BASE"]) 44 45# python_build: (Boolean) if true, we're either building Python or 46# building an extension with an un-installed Python, so we use 47# different (hard-wired) directories. 48# Setup.local is available for Makefile builds including VPATH builds, 49# Setup.dist is available on Windows 50def _python_build(): 51 for fn in ("Setup.dist", "Setup.local"): 52 if os.path.isfile(os.path.join(project_base, "Modules", fn)): 53 return True 54 return False 55python_build = _python_build() 56 57 58def get_python_version(): 59 """Return a string containing the major and minor Python version, 60 leaving off the patchlevel. Sample return values could be '1.5' 61 or '2.2'. 62 """ 63 return sys.version[:3] 64 65 66def get_python_inc(plat_specific=0, prefix=None): 67 """Return the directory containing installed Python header files. 68 69 If 'plat_specific' is false (the default), this is the path to the 70 non-platform-specific header files, i.e. Python.h and so on; 71 otherwise, this is the path to platform-specific header files 72 (namely pyconfig.h). 73 74 If 'prefix' is supplied, use it instead of sys.prefix or 75 sys.exec_prefix -- i.e., ignore 'plat_specific'. 76 """ 77 if prefix is None: 78 prefix = plat_specific and EXEC_PREFIX or PREFIX 79 80 # GCC(mingw): os.name is "nt" but build system is posix 81 if os.name == "posix" or sys.version.find('GCC') >= 0: 82 if python_build: 83 # NOTE: sysconfig.py-20091210 84 # Assume the executable is in the build directory. The 85 # pyconfig.h file should be in the same directory. Since 86 # the build directory may not be the source directory, we 87 # must use "srcdir" from the makefile to find the "Include" 88 # directory. 89 base = os.path.dirname(os.path.abspath(sys.executable)) 90 if plat_specific: 91 return base 92 else: 93 incdir = os.path.join(get_config_var('srcdir'), 'Include') 94 return os.path.normpath(incdir) 95 return os.path.join(prefix, "include", "python" + get_python_version()) 96 elif os.name == "nt": 97 return os.path.join(prefix, "include") 98 elif os.name == "os2": 99 return os.path.join(prefix, "Include") 100 else: 101 raise DistutilsPlatformError( 102 "I don't know where Python installs its C header files " 103 "on platform '%s'" % os.name) 104 105 106def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): 107 """Return the directory containing the Python library (standard or 108 site additions). 109 110 If 'plat_specific' is true, return the directory containing 111 platform-specific modules, i.e. any module from a non-pure-Python 112 module distribution; otherwise, return the platform-shared library 113 directory. If 'standard_lib' is true, return the directory 114 containing standard Python library modules; otherwise, return the 115 directory for site-specific modules. 116 117 If 'prefix' is supplied, use it instead of sys.prefix or 118 sys.exec_prefix -- i.e., ignore 'plat_specific'. 119 """ 120 if prefix is None: 121 prefix = plat_specific and EXEC_PREFIX or PREFIX 122 123 if os.name == "posix" or sys.version.find('GCC') >= 0: 124 libpython = os.path.join(prefix, 125 "lib", "python" + get_python_version()) 126 if standard_lib: 127 return libpython 128 else: 129 return os.path.join(libpython, "site-packages") 130 131 elif os.name == "nt": 132 if standard_lib: 133 return os.path.join(prefix, "Lib") 134 else: 135 if get_python_version() < "2.2": 136 return prefix 137 else: 138 return os.path.join(prefix, "Lib", "site-packages") 139 140 elif os.name == "os2": 141 if standard_lib: 142 return os.path.join(prefix, "Lib") 143 else: 144 return os.path.join(prefix, "Lib", "site-packages") 145 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 NOTE: (known limitation of python build/install system) 160 In cross-build environment make macros like CC and LDSHARED 161 contain cross-compiler/linker instead of host compiler/linker. 162 """ 163 posix_build = None 164 if compiler.compiler_type == "unix": 165 posix_build = True 166 elif compiler.compiler_type == "mingw32": 167 if sys.version.find('GCC') >= 0: 168 posix_build = True 169 if posix_build == True: 170 if sys.platform == "darwin": 171 # Perform first-time customization of compiler-related 172 # config vars on OS X now that we know we need a compiler. 173 # This is primarily to support Pythons from binary 174 # installers. The kind and paths to build tools on 175 # the user system may vary significantly from the system 176 # that Python itself was built on. Also the user OS 177 # version and build tools may not support the same set 178 # of CPU architectures for universal builds. 179 global _config_vars 180 if not _config_vars.get('CUSTOMIZED_OSX_COMPILER', ''): 181 import _osx_support 182 _osx_support.customize_compiler(_config_vars) 183 _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' 184 185 (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ 186 get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', 187 'CCSHARED', 'LDSHARED', 'SO', 'AR', 188 'ARFLAGS') 189 190 newcc = None 191 if 'CC' in os.environ: 192 cc = os.environ['CC'] 193 if 'CXX' in os.environ: 194 cxx = os.environ['CXX'] 195 if 'LDSHARED' in os.environ: 196 ldshared = os.environ['LDSHARED'] 197 if 'CPP' in os.environ: 198 cpp = os.environ['CPP'] 199 else: 200 cpp = cc + " -E" # not always 201 if 'LDFLAGS' in os.environ: 202 ldshared = ldshared + ' ' + os.environ['LDFLAGS'] 203 if 'CFLAGS' in os.environ: 204 cflags = opt + ' ' + os.environ['CFLAGS'] 205 ldshared = ldshared + ' ' + os.environ['CFLAGS'] 206 if 'CPPFLAGS' in os.environ: 207 cpp = cpp + ' ' + os.environ['CPPFLAGS'] 208 cflags = cflags + ' ' + os.environ['CPPFLAGS'] 209 ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] 210 if 'AR' in os.environ: 211 ar = os.environ['AR'] 212 if 'ARFLAGS' in os.environ: 213 archiver = ar + ' ' + os.environ['ARFLAGS'] 214 else: 215 archiver = ar + ' ' + ar_flags 216 217 cc_cmd = cc + ' ' + cflags 218 compiler.set_executables( 219 preprocessor=cpp, 220 compiler=cc_cmd, 221 compiler_so=cc_cmd + ' ' + ccshared, 222 compiler_cxx=cxx, 223 linker_so=ldshared, 224 linker_exe=cc, 225 archiver=archiver) 226 227 compiler.shared_lib_extension = so_ext 228 229 230def get_config_h_filename(): 231 """Return full pathname of installed pyconfig.h file.""" 232 if python_build: 233 # GCC(mingw): os.name is "nt" but build system is posix 234 if os.name == "nt" and sys.version.find('GCC') < 0: 235 inc_dir = os.path.join(project_base, "PC") 236 else: 237 inc_dir = project_base 238 else: 239 inc_dir = get_python_inc(plat_specific=1) 240 if get_python_version() < '2.2': 241 config_h = 'config.h' 242 else: 243 # The name of the config.h file changed in 2.2 244 config_h = 'pyconfig.h' 245 return os.path.join(inc_dir, config_h) 246 247 248def get_makefile_filename(): 249 """Return full pathname of installed Makefile from the Python build.""" 250 if python_build: 251 return os.path.join(project_base, "Makefile") 252 lib_dir = get_python_lib(plat_specific=1, standard_lib=1) 253 return os.path.join(lib_dir, "config", "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 1: 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("([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) 300 301 if g is None: 302 g = {} 303 done = {} 304 notdone = {} 305 306 while 1: 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 # do variable interpolation here 329 while notdone: 330 for name in notdone.keys(): 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 else: 345 done[n] = item = "" 346 if found: 347 after = value[m.end():] 348 value = value[:m.start()] + item + after 349 if "$" in after: 350 notdone[name] = value 351 else: 352 try: value = int(value) 353 except ValueError: 354 done[name] = value.strip() 355 else: 356 done[name] = value 357 del notdone[name] 358 else: 359 # bogus variable reference; just drop it since we can't deal 360 del notdone[name] 361 362 fp.close() 363 364 # strip spurious spaces 365 for k, v in done.items(): 366 if isinstance(v, str): 367 done[k] = v.strip() 368 369 # save the results in the global dictionary 370 g.update(done) 371 return g 372 373 374def expand_makefile_vars(s, vars): 375 """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in 376 'string' according to 'vars' (a dictionary mapping variable names to 377 values). Variables not present in 'vars' are silently expanded to the 378 empty string. The variable values in 'vars' should not contain further 379 variable expansions; if 'vars' is the output of 'parse_makefile()', 380 you're fine. Returns a variable-expanded version of 's'. 381 """ 382 383 # This algorithm does multiple expansion, so if vars['foo'] contains 384 # "${bar}", it will expand ${foo} to ${bar}, and then expand 385 # ${bar}... and so forth. This is fine as long as 'vars' comes from 386 # 'parse_makefile()', which takes care of such expansions eagerly, 387 # according to make's variable expansion semantics. 388 389 while 1: 390 m = _findvar1_rx.search(s) or _findvar2_rx.search(s) 391 if m: 392 (beg, end) = m.span() 393 s = s[0:beg] + vars.get(m.group(1)) + s[end:] 394 else: 395 break 396 return s 397 398 399_config_vars = None 400 401def _init_posix(): 402 """Initialize the module as appropriate for POSIX systems.""" 403 # _sysconfigdata is generated at build time, see the sysconfig module 404 from _sysconfigdata import build_time_vars 405 global _config_vars 406 _config_vars = {} 407 _config_vars.update(build_time_vars) 408 409 410def _init_nt(): 411 """Initialize the module as appropriate for NT""" 412 if sys.version.find('GCC') >= 0: 413 # GCC(mingw) use posix build system 414 # FIXME: may be modification has to be in get_config_vars ? 415 _init_posix() 416 return 417 g = {} 418 # set basic install directories 419 g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) 420 g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) 421 422 # XXX hmmm.. a normal install puts include files here 423 g['INCLUDEPY'] = get_python_inc(plat_specific=0) 424 425 g['SO'] = '.pyd' 426 g['EXE'] = ".exe" 427 g['VERSION'] = get_python_version().replace(".", "") 428 g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) 429 430 global _config_vars 431 _config_vars = g 432 433 434def _init_os2(): 435 """Initialize the module as appropriate for OS/2""" 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['SO'] = '.pyd' 445 g['EXE'] = ".exe" 446 447 global _config_vars 448 _config_vars = g 449 450 451def get_config_vars(*args): 452 """With no arguments, return a dictionary of all configuration 453 variables relevant for the current platform. Generally this includes 454 everything needed to build extensions and install both pure modules and 455 extensions. On Unix, this means every variable defined in Python's 456 installed Makefile; on Windows and Mac OS it's a much smaller set. 457 458 With arguments, return a list of values that result from looking up 459 each argument in the configuration variable dictionary. 460 """ 461 global _config_vars 462 if _config_vars is None: 463 func = globals().get("_init_" + os.name) 464 if func: 465 func() 466 else: 467 _config_vars = {} 468 469 # Normalized versions of prefix and exec_prefix are handy to have; 470 # in fact, these are the standard versions used most places in the 471 # Distutils. 472 _config_vars['prefix'] = PREFIX 473 _config_vars['exec_prefix'] = EXEC_PREFIX 474 475 # OS X platforms require special customization to handle 476 # multi-architecture, multi-os-version installers 477 if sys.platform == 'darwin': 478 import _osx_support 479 _osx_support.customize_config_vars(_config_vars) 480 481 if args: 482 vals = [] 483 for name in args: 484 vals.append(_config_vars.get(name)) 485 return vals 486 else: 487 return _config_vars 488 489def get_config_var(name): 490 """Return the value of a single variable using the dictionary 491 returned by 'get_config_vars()'. Equivalent to 492 get_config_vars().get(name) 493 """ 494 return get_config_vars().get(name) 495