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 # python_build: (Boolean) if true, we're either building Python or

     41 # building an extension with an un-installed Python, so we use

     42 # different (hard-wired) directories.

     43 # Setup.local is available for Makefile builds including VPATH builds,

     44 # Setup.dist is available on Windows

     45 def _python_build():
     46     for fn in ("Setup.dist", "Setup.local"):
     47         if os.path.isfile(os.path.join(project_base, "Modules", fn)):
     48             return True
     49     return False
     50 python_build = _python_build()
     51 
     52 
     53 def get_python_version():
     54     """Return a string containing the major and minor Python version,
     55     leaving off the patchlevel.  Sample return values could be '1.5'
     56     or '2.2'.
     57     """
     58     return sys.version[:3]
     59 
     60 
     61 def get_python_inc(plat_specific=0, prefix=None):
     62     """Return the directory containing installed Python header files.
     63 
     64     If 'plat_specific' is false (the default), this is the path to the
     65     non-platform-specific header files, i.e. Python.h and so on;
     66     otherwise, this is the path to platform-specific header files
     67     (namely pyconfig.h).
     68 
     69     If 'prefix' is supplied, use it instead of sys.prefix or
     70     sys.exec_prefix -- i.e., ignore 'plat_specific'.
     71     """
     72     if prefix is None:
     73         prefix = plat_specific and EXEC_PREFIX or PREFIX
     74 
     75     if os.name == "posix":
     76         if python_build:
     77             buildir = os.path.dirname(sys.executable)
     78             if plat_specific:
     79                 # python.h is located in the buildir

     80                 inc_dir = buildir
     81             else:
     82                 # the source dir is relative to the buildir

     83                 srcdir = os.path.abspath(os.path.join(buildir,
     84                                          get_config_var('srcdir')))
     85                 # Include is located in the srcdir

     86                 inc_dir = os.path.join(srcdir, "Include")
     87             return inc_dir
     88         return os.path.join(prefix, "include", "python" + get_python_version())
     89     elif os.name == "nt":
     90         return os.path.join(prefix, "include")
     91     elif os.name == "os2":
     92         return os.path.join(prefix, "Include")
     93     else:
     94         raise DistutilsPlatformError(
     95             "I don't know where Python installs its C header files "
     96             "on platform '%s'" % os.name)
     97 
     98 
     99 def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
    100     """Return the directory containing the Python library (standard or
    101     site additions).
    102 
    103     If 'plat_specific' is true, return the directory containing
    104     platform-specific modules, i.e. any module from a non-pure-Python
    105     module distribution; otherwise, return the platform-shared library
    106     directory.  If 'standard_lib' is true, return the directory
    107     containing standard Python library modules; otherwise, return the
    108     directory for site-specific modules.
    109 
    110     If 'prefix' is supplied, use it instead of sys.prefix or
    111     sys.exec_prefix -- i.e., ignore 'plat_specific'.
    112     """
    113     if prefix is None:
    114         prefix = plat_specific and EXEC_PREFIX or PREFIX
    115 
    116     if os.name == "posix":
    117         libpython = os.path.join(prefix,
    118                                  "lib", "python" + get_python_version())
    119         if standard_lib:
    120             return libpython
    121         else:
    122             return os.path.join(libpython, "site-packages")
    123 
    124     elif os.name == "nt":
    125         if standard_lib:
    126             return os.path.join(prefix, "Lib")
    127         else:
    128             if get_python_version() < "2.2":
    129                 return prefix
    130             else:
    131                 return os.path.join(prefix, "Lib", "site-packages")
    132 
    133     elif os.name == "os2":
    134         if standard_lib:
    135             return os.path.join(prefix, "Lib")
    136         else:
    137             return os.path.join(prefix, "Lib", "site-packages")
    138 
    139     else:
    140         raise DistutilsPlatformError(
    141             "I don't know where Python installs its library "
    142             "on platform '%s'" % os.name)
    143 
    144 
    145 def customize_compiler(compiler):
    146     """Do any platform-specific customization of a CCompiler instance.
    147 
    148     Mainly needed on Unix, so we can plug in the information that
    149     varies across Unices and is stored in Python's Makefile.
    150     """
    151     if compiler.compiler_type == "unix":
    152         (cc, cxx, opt, cflags, ccshared, ldshared, so_ext) = \
    153             get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
    154                             'CCSHARED', 'LDSHARED', 'SO')
    155 
    156         if 'CC' in os.environ:
    157             cc = os.environ['CC']
    158         if 'CXX' in os.environ:
    159             cxx = os.environ['CXX']
    160         if 'LDSHARED' in os.environ:
    161             ldshared = os.environ['LDSHARED']
    162         if 'CPP' in os.environ:
    163             cpp = os.environ['CPP']
    164         else:
    165             cpp = cc + " -E"           # not always

    166         if 'LDFLAGS' in os.environ:
    167             ldshared = ldshared + ' ' + os.environ['LDFLAGS']
    168         if 'CFLAGS' in os.environ:
    169             cflags = opt + ' ' + os.environ['CFLAGS']
    170             ldshared = ldshared + ' ' + os.environ['CFLAGS']
    171         if 'CPPFLAGS' in os.environ:
    172             cpp = cpp + ' ' + os.environ['CPPFLAGS']
    173             cflags = cflags + ' ' + os.environ['CPPFLAGS']
    174             ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
    175 
    176         cc_cmd = cc + ' ' + cflags
    177         compiler.set_executables(
    178             preprocessor=cpp,
    179             compiler=cc_cmd,
    180             compiler_so=cc_cmd + ' ' + ccshared,
    181             compiler_cxx=cxx,
    182             linker_so=ldshared,
    183             linker_exe=cc)
    184 
    185         compiler.shared_lib_extension = so_ext
    186 
    187 
    188 def get_config_h_filename():
    189     """Return full pathname of installed pyconfig.h file."""
    190     if python_build:
    191         if os.name == "nt":
    192             inc_dir = os.path.join(project_base, "PC")
    193         else:
    194             inc_dir = project_base
    195     else:
    196         inc_dir = get_python_inc(plat_specific=1)
    197     if get_python_version() < '2.2':
    198         config_h = 'config.h'
    199     else:
    200         # The name of the config.h file changed in 2.2

    201         config_h = 'pyconfig.h'
    202     return os.path.join(inc_dir, config_h)
    203 
    204 
    205 def get_makefile_filename():
    206     """Return full pathname of installed Makefile from the Python build."""
    207     if python_build:
    208         return os.path.join(os.path.dirname(sys.executable), "Makefile")
    209     lib_dir = get_python_lib(plat_specific=1, standard_lib=1)
    210     return os.path.join(lib_dir, "config", "Makefile")
    211 
    212 
    213 def parse_config_h(fp, g=None):
    214     """Parse a config.h-style file.
    215 
    216     A dictionary containing name/value pairs is returned.  If an
    217     optional dictionary is passed in as the second argument, it is
    218     used instead of a new dictionary.
    219     """
    220     if g is None:
    221         g = {}
    222     define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
    223     undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
    224     #

    225     while 1:
    226         line = fp.readline()
    227         if not line:
    228             break
    229         m = define_rx.match(line)
    230         if m:
    231             n, v = m.group(1, 2)
    232             try: v = int(v)
    233             except ValueError: pass
    234             g[n] = v
    235         else:
    236             m = undef_rx.match(line)
    237             if m:
    238                 g[m.group(1)] = 0
    239     return g
    240 
    241 
    242 # Regexes needed for parsing Makefile (and similar syntaxes,

    243 # like old-style Setup files).

    244 _variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
    245 _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
    246 _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
    247 
    248 def parse_makefile(fn, g=None):
    249     """Parse a Makefile-style file.
    250 
    251     A dictionary containing name/value pairs is returned.  If an
    252     optional dictionary is passed in as the second argument, it is
    253     used instead of a new dictionary.
    254     """
    255     from distutils.text_file import TextFile
    256     fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1)
    257 
    258     if g is None:
    259         g = {}
    260     done = {}
    261     notdone = {}
    262 
    263     while 1:
    264         line = fp.readline()
    265         if line is None:  # eof

    266             break
    267         m = _variable_rx.match(line)
    268         if m:
    269             n, v = m.group(1, 2)
    270             v = v.strip()
    271             # `$$' is a literal `$' in make

    272             tmpv = v.replace('$$', '')
    273 
    274             if "$" in tmpv:
    275                 notdone[n] = v
    276             else:
    277                 try:
    278                     v = int(v)
    279                 except ValueError:
    280                     # insert literal `$'

    281                     done[n] = v.replace('$$', '$')
    282                 else:
    283                     done[n] = v
    284 
    285     # do variable interpolation here

    286     while notdone:
    287         for name in notdone.keys():
    288             value = notdone[name]
    289             m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
    290             if m:
    291                 n = m.group(1)
    292                 found = True
    293                 if n in done:
    294                     item = str(done[n])
    295                 elif n in notdone:
    296                     # get it on a subsequent round

    297                     found = False
    298                 elif n in os.environ:
    299                     # do it like make: fall back to environment

    300                     item = os.environ[n]
    301                 else:
    302                     done[n] = item = ""
    303                 if found:
    304                     after = value[m.end():]
    305                     value = value[:m.start()] + item + after
    306                     if "$" in after:
    307                         notdone[name] = value
    308                     else:
    309                         try: value = int(value)
    310                         except ValueError:
    311                             done[name] = value.strip()
    312                         else:
    313                             done[name] = value
    314                         del notdone[name]
    315             else:
    316                 # bogus variable reference; just drop it since we can't deal

    317                 del notdone[name]
    318 
    319     fp.close()
    320 
    321     # strip spurious spaces

    322     for k, v in done.items():
    323         if isinstance(v, str):
    324             done[k] = v.strip()
    325 
    326     # save the results in the global dictionary

    327     g.update(done)
    328     return g
    329 
    330 
    331 def expand_makefile_vars(s, vars):
    332     """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
    333     'string' according to 'vars' (a dictionary mapping variable names to
    334     values).  Variables not present in 'vars' are silently expanded to the
    335     empty string.  The variable values in 'vars' should not contain further
    336     variable expansions; if 'vars' is the output of 'parse_makefile()',
    337     you're fine.  Returns a variable-expanded version of 's'.
    338     """
    339 
    340     # This algorithm does multiple expansion, so if vars['foo'] contains

    341     # "${bar}", it will expand ${foo} to ${bar}, and then expand

    342     # ${bar}... and so forth.  This is fine as long as 'vars' comes from

    343     # 'parse_makefile()', which takes care of such expansions eagerly,

    344     # according to make's variable expansion semantics.

    345 
    346     while 1:
    347         m = _findvar1_rx.search(s) or _findvar2_rx.search(s)
    348         if m:
    349             (beg, end) = m.span()
    350             s = s[0:beg] + vars.get(m.group(1)) + s[end:]
    351         else:
    352             break
    353     return s
    354 
    355 
    356 _config_vars = None
    357 
    358 def _init_posix():
    359     """Initialize the module as appropriate for POSIX systems."""
    360     g = {}
    361     # load the installed Makefile:

    362     try:
    363         filename = get_makefile_filename()
    364         parse_makefile(filename, g)
    365     except IOError, msg:
    366         my_msg = "invalid Python installation: unable to open %s" % filename
    367         if hasattr(msg, "strerror"):
    368             my_msg = my_msg + " (%s)" % msg.strerror
    369 
    370         raise DistutilsPlatformError(my_msg)
    371 
    372     # load the installed pyconfig.h:

    373     try:
    374         filename = get_config_h_filename()
    375         parse_config_h(file(filename), g)
    376     except IOError, msg:
    377         my_msg = "invalid Python installation: unable to open %s" % filename
    378         if hasattr(msg, "strerror"):
    379             my_msg = my_msg + " (%s)" % msg.strerror
    380 
    381         raise DistutilsPlatformError(my_msg)
    382 
    383     # On MacOSX we need to check the setting of the environment variable

    384     # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so

    385     # it needs to be compatible.

    386     # If it isn't set we set it to the configure-time value

    387     if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in g:
    388         cfg_target = g['MACOSX_DEPLOYMENT_TARGET']
    389         cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '')
    390         if cur_target == '':
    391             cur_target = cfg_target
    392             os.environ['MACOSX_DEPLOYMENT_TARGET'] = cfg_target
    393         elif map(int, cfg_target.split('.')) > map(int, cur_target.split('.')):
    394             my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure'
    395                 % (cur_target, cfg_target))
    396             raise DistutilsPlatformError(my_msg)
    397 
    398     # On AIX, there are wrong paths to the linker scripts in the Makefile

    399     # -- these paths are relative to the Python source, but when installed

    400     # the scripts are in another directory.

    401     if python_build:
    402         g['LDSHARED'] = g['BLDSHARED']
    403 
    404     elif get_python_version() < '2.1':
    405         # The following two branches are for 1.5.2 compatibility.

    406         if sys.platform == 'aix4':          # what about AIX 3.x ?

    407             # Linker script is in the config directory, not in Modules as the

    408             # Makefile says.

    409             python_lib = get_python_lib(standard_lib=1)
    410             ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix')
    411             python_exp = os.path.join(python_lib, 'config', 'python.exp')
    412 
    413             g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp)
    414 
    415         elif sys.platform == 'beos':
    416             # Linker script is in the config directory.  In the Makefile it is

    417             # relative to the srcdir, which after installation no longer makes

    418             # sense.

    419             python_lib = get_python_lib(standard_lib=1)
    420             linkerscript_path = string.split(g['LDSHARED'])[0]
    421             linkerscript_name = os.path.basename(linkerscript_path)
    422             linkerscript = os.path.join(python_lib, 'config',
    423                                         linkerscript_name)
    424 
    425             # XXX this isn't the right place to do this: adding the Python

    426             # library to the link, if needed, should be in the "build_ext"

    427             # command.  (It's also needed for non-MS compilers on Windows, and

    428             # it's taken care of for them by the 'build_ext.get_libraries()'

    429             # method.)

    430             g['LDSHARED'] = ("%s -L%s/lib -lpython%s" %
    431                              (linkerscript, PREFIX, get_python_version()))
    432 
    433     global _config_vars
    434     _config_vars = g
    435 
    436 
    437 def _init_nt():
    438     """Initialize the module as appropriate for NT"""
    439     g = {}
    440     # set basic install directories

    441     g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
    442     g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
    443 
    444     # XXX hmmm.. a normal install puts include files here

    445     g['INCLUDEPY'] = get_python_inc(plat_specific=0)
    446 
    447     g['SO'] = '.pyd'
    448     g['EXE'] = ".exe"
    449     g['VERSION'] = get_python_version().replace(".", "")
    450     g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable))
    451 
    452     global _config_vars
    453     _config_vars = g
    454 
    455 
    456 def _init_os2():
    457     """Initialize the module as appropriate for OS/2"""
    458     g = {}
    459     # set basic install directories

    460     g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
    461     g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
    462 
    463     # XXX hmmm.. a normal install puts include files here

    464     g['INCLUDEPY'] = get_python_inc(plat_specific=0)
    465 
    466     g['SO'] = '.pyd'
    467     g['EXE'] = ".exe"
    468 
    469     global _config_vars
    470     _config_vars = g
    471 
    472 
    473 def get_config_vars(*args):
    474     """With no arguments, return a dictionary of all configuration
    475     variables relevant for the current platform.  Generally this includes
    476     everything needed to build extensions and install both pure modules and
    477     extensions.  On Unix, this means every variable defined in Python's
    478     installed Makefile; on Windows and Mac OS it's a much smaller set.
    479 
    480     With arguments, return a list of values that result from looking up
    481     each argument in the configuration variable dictionary.
    482     """
    483     global _config_vars
    484     if _config_vars is None:
    485         func = globals().get("_init_" + os.name)
    486         if func:
    487             func()
    488         else:
    489             _config_vars = {}
    490 
    491         # Normalized versions of prefix and exec_prefix are handy to have;

    492         # in fact, these are the standard versions used most places in the

    493         # Distutils.

    494         _config_vars['prefix'] = PREFIX
    495         _config_vars['exec_prefix'] = EXEC_PREFIX
    496 
    497         if sys.platform == 'darwin':
    498             kernel_version = os.uname()[2] # Kernel version (8.4.3)

    499             major_version = int(kernel_version.split('.')[0])
    500 
    501             if major_version < 8:
    502                 # On Mac OS X before 10.4, check if -arch and -isysroot

    503                 # are in CFLAGS or LDFLAGS and remove them if they are.

    504                 # This is needed when building extensions on a 10.3 system

    505                 # using a universal build of python.

    506                 for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED',
    507                         # a number of derived variables. These need to be

    508                         # patched up as well.

    509                         'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
    510                     flags = _config_vars[key]
    511                     flags = re.sub('-arch\s+\w+\s', ' ', flags)
    512                     flags = re.sub('-isysroot [^ \t]*', ' ', flags)
    513                     _config_vars[key] = flags
    514 
    515             else:
    516 
    517                 # Allow the user to override the architecture flags using

    518                 # an environment variable.

    519                 # NOTE: This name was introduced by Apple in OSX 10.5 and

    520                 # is used by several scripting languages distributed with

    521                 # that OS release.

    522 
    523                 if 'ARCHFLAGS' in os.environ:
    524                     arch = os.environ['ARCHFLAGS']
    525                     for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED',
    526                         # a number of derived variables. These need to be

    527                         # patched up as well.

    528                         'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
    529 
    530                         flags = _config_vars[key]
    531                         flags = re.sub('-arch\s+\w+\s', ' ', flags)
    532                         flags = flags + ' ' + arch
    533                         _config_vars[key] = flags
    534 
    535                 # If we're on OSX 10.5 or later and the user tries to

    536                 # compiles an extension using an SDK that is not present

    537                 # on the current machine it is better to not use an SDK

    538                 # than to fail.

    539                 #

    540                 # The major usecase for this is users using a Python.org

    541                 # binary installer  on OSX 10.6: that installer uses

    542                 # the 10.4u SDK, but that SDK is not installed by default

    543                 # when you install Xcode.

    544                 #

    545                 m = re.search('-isysroot\s+(\S+)', _config_vars['CFLAGS'])
    546                 if m is not None:
    547                     sdk = m.group(1)
    548                     if not os.path.exists(sdk):
    549                         for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED',
    550                              # a number of derived variables. These need to be

    551                              # patched up as well.

    552                             'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
    553 
    554                             flags = _config_vars[key]
    555                             flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags)
    556                             _config_vars[key] = flags
    557 
    558     if args:
    559         vals = []
    560         for name in args:
    561             vals.append(_config_vars.get(name))
    562         return vals
    563     else:
    564         return _config_vars
    565 
    566 def get_config_var(name):
    567     """Return the value of a single variable using the dictionary
    568     returned by 'get_config_vars()'.  Equivalent to
    569     get_config_vars().get(name)
    570     """
    571     return get_config_vars().get(name)
    572