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     if os.name == "posix":
     81         if python_build:
     82             buildir = os.path.dirname(sys.executable)
     83             if plat_specific:
     84                 # python.h is located in the buildir
     85                 inc_dir = buildir
     86             else:
     87                 # the source dir is relative to the buildir
     88                 srcdir = os.path.abspath(os.path.join(buildir,
     89                                          get_config_var('srcdir')))
     90                 # Include is located in the srcdir
     91                 inc_dir = os.path.join(srcdir, "Include")
     92             return inc_dir
     93         return os.path.join(prefix, "include", "python" + get_python_version())
     94     elif os.name == "nt":
     95         return os.path.join(prefix, "include")
     96     elif os.name == "os2":
     97         return os.path.join(prefix, "Include")
     98     else:
     99         raise DistutilsPlatformError(
    100             "I don't know where Python installs its C header files "
    101             "on platform '%s'" % os.name)
    102 
    103 
    104 def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
    105     """Return the directory containing the Python library (standard or
    106     site additions).
    107 
    108     If 'plat_specific' is true, return the directory containing
    109     platform-specific modules, i.e. any module from a non-pure-Python
    110     module distribution; otherwise, return the platform-shared library
    111     directory.  If 'standard_lib' is true, return the directory
    112     containing standard Python library modules; otherwise, return the
    113     directory for site-specific modules.
    114 
    115     If 'prefix' is supplied, use it instead of sys.prefix or
    116     sys.exec_prefix -- i.e., ignore 'plat_specific'.
    117     """
    118     if prefix is None:
    119         prefix = plat_specific and EXEC_PREFIX or PREFIX
    120 
    121     if os.name == "posix":
    122         libpython = os.path.join(prefix,
    123                                  "lib", "python" + get_python_version())
    124         if standard_lib:
    125             return libpython
    126         else:
    127             return os.path.join(libpython, "site-packages")
    128 
    129     elif os.name == "nt":
    130         if standard_lib:
    131             return os.path.join(prefix, "Lib")
    132         else:
    133             if get_python_version() < "2.2":
    134                 return prefix
    135             else:
    136                 return os.path.join(prefix, "Lib", "site-packages")
    137 
    138     elif os.name == "os2":
    139         if standard_lib:
    140             return os.path.join(prefix, "Lib")
    141         else:
    142             return os.path.join(prefix, "Lib", "site-packages")
    143 
    144     else:
    145         raise DistutilsPlatformError(
    146             "I don't know where Python installs its library "
    147             "on platform '%s'" % os.name)
    148 
    149 
    150 
    151 def customize_compiler(compiler):
    152     """Do any platform-specific customization of a CCompiler instance.
    153 
    154     Mainly needed on Unix, so we can plug in the information that
    155     varies across Unices and is stored in Python's Makefile.
    156     """
    157     if compiler.compiler_type == "unix":
    158         if sys.platform == "darwin":
    159             # Perform first-time customization of compiler-related
    160             # config vars on OS X now that we know we need a compiler.
    161             # This is primarily to support Pythons from binary
    162             # installers.  The kind and paths to build tools on
    163             # the user system may vary significantly from the system
    164             # that Python itself was built on.  Also the user OS
    165             # version and build tools may not support the same set
    166             # of CPU architectures for universal builds.
    167             global _config_vars
    168             # Use get_config_var() to ensure _config_vars is initialized.
    169             if not get_config_var('CUSTOMIZED_OSX_COMPILER'):
    170                 import _osx_support
    171                 _osx_support.customize_compiler(_config_vars)
    172                 _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
    173 
    174         (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \
    175             get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
    176                             'CCSHARED', 'LDSHARED', 'SO', 'AR',
    177                             'ARFLAGS')
    178 
    179         if 'CC' in os.environ:
    180             newcc = os.environ['CC']
    181             if (sys.platform == 'darwin'
    182                     and 'LDSHARED' not in os.environ
    183                     and ldshared.startswith(cc)):
    184                 # On OS X, if CC is overridden, use that as the default
    185                 #       command for LDSHARED as well
    186                 ldshared = newcc + ldshared[len(cc):]
    187             cc = newcc
    188         if 'CXX' in os.environ:
    189             cxx = os.environ['CXX']
    190         if 'LDSHARED' in os.environ:
    191             ldshared = os.environ['LDSHARED']
    192         if 'CPP' in os.environ:
    193             cpp = os.environ['CPP']
    194         else:
    195             cpp = cc + " -E"           # not always
    196         if 'LDFLAGS' in os.environ:
    197             ldshared = ldshared + ' ' + os.environ['LDFLAGS']
    198         if 'CFLAGS' in os.environ:
    199             cflags = opt + ' ' + os.environ['CFLAGS']
    200             ldshared = ldshared + ' ' + os.environ['CFLAGS']
    201         if 'CPPFLAGS' in os.environ:
    202             cpp = cpp + ' ' + os.environ['CPPFLAGS']
    203             cflags = cflags + ' ' + os.environ['CPPFLAGS']
    204             ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
    205         if 'AR' in os.environ:
    206             ar = os.environ['AR']
    207         if 'ARFLAGS' in os.environ:
    208             archiver = ar + ' ' + os.environ['ARFLAGS']
    209         else:
    210             archiver = ar + ' ' + ar_flags
    211 
    212         cc_cmd = cc + ' ' + cflags
    213         compiler.set_executables(
    214             preprocessor=cpp,
    215             compiler=cc_cmd,
    216             compiler_so=cc_cmd + ' ' + ccshared,
    217             compiler_cxx=cxx,
    218             linker_so=ldshared,
    219             linker_exe=cc,
    220             archiver=archiver)
    221 
    222         compiler.shared_lib_extension = so_ext
    223 
    224 
    225 def get_config_h_filename():
    226     """Return full pathname of installed pyconfig.h file."""
    227     if python_build:
    228         if os.name == "nt":
    229             inc_dir = os.path.join(project_base, "PC")
    230         else:
    231             inc_dir = project_base
    232     else:
    233         inc_dir = get_python_inc(plat_specific=1)
    234     if get_python_version() < '2.2':
    235         config_h = 'config.h'
    236     else:
    237         # The name of the config.h file changed in 2.2
    238         config_h = 'pyconfig.h'
    239     return os.path.join(inc_dir, config_h)
    240 
    241 
    242 def get_makefile_filename():
    243     """Return full pathname of installed Makefile from the Python build."""
    244     if python_build:
    245         return os.path.join(project_base, "Makefile")
    246     lib_dir = get_python_lib(plat_specific=1, standard_lib=1)
    247     return os.path.join(lib_dir, "config", "Makefile")
    248 
    249 
    250 def 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 1:
    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("([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 
    285 def 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)
    294 
    295     if g is None:
    296         g = {}
    297     done = {}
    298     notdone = {}
    299 
    300     while 1:
    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     # do variable interpolation here
    323     while notdone:
    324         for name in notdone.keys():
    325             value = notdone[name]
    326             m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
    327             if m:
    328                 n = m.group(1)
    329                 found = True
    330                 if n in done:
    331                     item = str(done[n])
    332                 elif n in notdone:
    333                     # get it on a subsequent round
    334                     found = False
    335                 elif n in os.environ:
    336                     # do it like make: fall back to environment
    337                     item = os.environ[n]
    338                 else:
    339                     done[n] = item = ""
    340                 if found:
    341                     after = value[m.end():]
    342                     value = value[:m.start()] + item + after
    343                     if "$" in after:
    344                         notdone[name] = value
    345                     else:
    346                         try: value = int(value)
    347                         except ValueError:
    348                             done[name] = value.strip()
    349                         else:
    350                             done[name] = value
    351                         del notdone[name]
    352             else:
    353                 # bogus variable reference; just drop it since we can't deal
    354                 del notdone[name]
    355 
    356     fp.close()
    357 
    358     # strip spurious spaces
    359     for k, v in done.items():
    360         if isinstance(v, str):
    361             done[k] = v.strip()
    362 
    363     # save the results in the global dictionary
    364     g.update(done)
    365     return g
    366 
    367 
    368 def expand_makefile_vars(s, vars):
    369     """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
    370     'string' according to 'vars' (a dictionary mapping variable names to
    371     values).  Variables not present in 'vars' are silently expanded to the
    372     empty string.  The variable values in 'vars' should not contain further
    373     variable expansions; if 'vars' is the output of 'parse_makefile()',
    374     you're fine.  Returns a variable-expanded version of 's'.
    375     """
    376 
    377     # This algorithm does multiple expansion, so if vars['foo'] contains
    378     # "${bar}", it will expand ${foo} to ${bar}, and then expand
    379     # ${bar}... and so forth.  This is fine as long as 'vars' comes from
    380     # 'parse_makefile()', which takes care of such expansions eagerly,
    381     # according to make's variable expansion semantics.
    382 
    383     while 1:
    384         m = _findvar1_rx.search(s) or _findvar2_rx.search(s)
    385         if m:
    386             (beg, end) = m.span()
    387             s = s[0:beg] + vars.get(m.group(1)) + s[end:]
    388         else:
    389             break
    390     return s
    391 
    392 
    393 _config_vars = None
    394 
    395 def _init_posix():
    396     """Initialize the module as appropriate for POSIX systems."""
    397     # _sysconfigdata is generated at build time, see the sysconfig module
    398     from _sysconfigdata import build_time_vars
    399     global _config_vars
    400     _config_vars = {}
    401     _config_vars.update(build_time_vars)
    402 
    403 
    404 def _init_nt():
    405     """Initialize the module as appropriate for NT"""
    406     g = {}
    407     # set basic install directories
    408     g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
    409     g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
    410 
    411     # XXX hmmm.. a normal install puts include files here
    412     g['INCLUDEPY'] = get_python_inc(plat_specific=0)
    413 
    414     g['SO'] = '.pyd'
    415     g['EXE'] = ".exe"
    416     g['VERSION'] = get_python_version().replace(".", "")
    417     g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable))
    418 
    419     global _config_vars
    420     _config_vars = g
    421 
    422 
    423 def _init_os2():
    424     """Initialize the module as appropriate for OS/2"""
    425     g = {}
    426     # set basic install directories
    427     g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
    428     g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
    429 
    430     # XXX hmmm.. a normal install puts include files here
    431     g['INCLUDEPY'] = get_python_inc(plat_specific=0)
    432 
    433     g['SO'] = '.pyd'
    434     g['EXE'] = ".exe"
    435 
    436     global _config_vars
    437     _config_vars = g
    438 
    439 
    440 def get_config_vars(*args):
    441     """With no arguments, return a dictionary of all configuration
    442     variables relevant for the current platform.  Generally this includes
    443     everything needed to build extensions and install both pure modules and
    444     extensions.  On Unix, this means every variable defined in Python's
    445     installed Makefile; on Windows and Mac OS it's a much smaller set.
    446 
    447     With arguments, return a list of values that result from looking up
    448     each argument in the configuration variable dictionary.
    449     """
    450     global _config_vars
    451     if _config_vars is None:
    452         func = globals().get("_init_" + os.name)
    453         if func:
    454             func()
    455         else:
    456             _config_vars = {}
    457 
    458         # Normalized versions of prefix and exec_prefix are handy to have;
    459         # in fact, these are the standard versions used most places in the
    460         # Distutils.
    461         _config_vars['prefix'] = PREFIX
    462         _config_vars['exec_prefix'] = EXEC_PREFIX
    463 
    464         # OS X platforms require special customization to handle
    465         # multi-architecture, multi-os-version installers
    466         if sys.platform == 'darwin':
    467             import _osx_support
    468             _osx_support.customize_config_vars(_config_vars)
    469 
    470     if args:
    471         vals = []
    472         for name in args:
    473             vals.append(_config_vars.get(name))
    474         return vals
    475     else:
    476         return _config_vars
    477 
    478 def get_config_var(name):
    479     """Return the value of a single variable using the dictionary
    480     returned by 'get_config_vars()'.  Equivalent to
    481     get_config_vars().get(name)
    482     """
    483     return get_config_vars().get(name)
    484