Home | History | Annotate | Download | only in distutils
      1 """Provide access to Python's configuration information.  The specific
      2 configuration variables available depend heavily on the platform and
      3 configuration.  The values may be retrieved using
      4 get_config_var(name), and the list of variables is available via
      5 get_config_vars().keys().  Additional convenience functions are also
      6 available.
      7 
      8 Written by:   Fred L. Drake, Jr.
      9 Email:        <fdrake (at] acm.org>
     10 """
     11 
     12 __revision__ = "$Id$"
     13 
     14 import os
     15 import re
     16 import string
     17 import sys
     18 
     19 from distutils.errors import DistutilsPlatformError
     20 
     21 # These are needed in a couple of spots, so just compute them once.
     22 PREFIX = os.path.normpath(sys.prefix)
     23 EXEC_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.
     28 project_base = os.path.dirname(os.path.abspath(sys.executable))
     29 if 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
     32 if 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
     36 if 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
     41 if "_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
     50 def _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
     55 python_build = _python_build()
     56 
     57 
     58 def 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 
     66 def 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 
    106 def 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 
    153 def 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 
    230 def 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 
    248 def 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 
    256 def 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 
    291 def 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 
    374 def 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 
    401 def _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 
    410 def _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 
    434 def _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 
    451 def 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 
    489 def 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