• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    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
104def 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
151def 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
225def 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
242def 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
250def 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
285def 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
368def 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
395def _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
404def _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
423def _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
440def 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
478def 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