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