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.
28if sys.executable:
29    project_base = os.path.dirname(os.path.abspath(sys.executable))
30else:
31    # sys.executable can be empty if argv[0] has been changed and Python is
32    # unable to retrieve the real program name
33    project_base = os.getcwd()
34if os.name == "nt" and "pcbuild" in project_base[-8:].lower():
35    project_base = os.path.abspath(os.path.join(project_base, os.path.pardir))
36# PC/VS7.1
37if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower():
38    project_base = os.path.abspath(os.path.join(project_base, os.path.pardir,
39                                                os.path.pardir))
40# PC/AMD64
41if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower():
42    project_base = os.path.abspath(os.path.join(project_base, os.path.pardir,
43                                                os.path.pardir))
44
45# set for cross builds
46if "_PYTHON_PROJECT_BASE" in os.environ:
47    # this is the build directory, at least for posix
48    project_base = os.path.normpath(os.environ["_PYTHON_PROJECT_BASE"])
49
50# python_build: (Boolean) if true, we're either building Python or
51# building an extension with an un-installed Python, so we use
52# different (hard-wired) directories.
53# Setup.local is available for Makefile builds including VPATH builds,
54# Setup.dist is available on Windows
55def _python_build():
56    for fn in ("Setup.dist", "Setup.local"):
57        if os.path.isfile(os.path.join(project_base, "Modules", fn)):
58            return True
59    return False
60python_build = _python_build()
61
62
63def get_python_version():
64    """Return a string containing the major and minor Python version,
65    leaving off the patchlevel.  Sample return values could be '1.5'
66    or '2.2'.
67    """
68    return sys.version[:3]
69
70
71def get_python_inc(plat_specific=0, prefix=None):
72    """Return the directory containing installed Python header files.
73
74    If 'plat_specific' is false (the default), this is the path to the
75    non-platform-specific header files, i.e. Python.h and so on;
76    otherwise, this is the path to platform-specific header files
77    (namely pyconfig.h).
78
79    If 'prefix' is supplied, use it instead of sys.prefix or
80    sys.exec_prefix -- i.e., ignore 'plat_specific'.
81    """
82    if prefix is None:
83        prefix = plat_specific and EXEC_PREFIX or PREFIX
84
85    if os.name == "posix":
86        if python_build:
87            if sys.executable:
88                buildir = os.path.dirname(sys.executable)
89            else:
90                # sys.executable can be empty if argv[0] has been changed
91                # and Python is unable to retrieve the real program name
92                buildir = os.getcwd()
93            if plat_specific:
94                # python.h is located in the buildir
95                inc_dir = buildir
96            else:
97                # the source dir is relative to the buildir
98                srcdir = os.path.abspath(os.path.join(buildir,
99                                         get_config_var('srcdir')))
100                # Include is located in the srcdir
101                inc_dir = os.path.join(srcdir, "Include")
102            return inc_dir
103        return os.path.join(prefix, "include", "python" + get_python_version())
104    elif os.name == "nt":
105        return os.path.join(prefix, "include")
106    elif os.name == "os2":
107        return os.path.join(prefix, "Include")
108    else:
109        raise DistutilsPlatformError(
110            "I don't know where Python installs its C header files "
111            "on platform '%s'" % os.name)
112
113
114def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
115    """Return the directory containing the Python library (standard or
116    site additions).
117
118    If 'plat_specific' is true, return the directory containing
119    platform-specific modules, i.e. any module from a non-pure-Python
120    module distribution; otherwise, return the platform-shared library
121    directory.  If 'standard_lib' is true, return the directory
122    containing standard Python library modules; otherwise, return the
123    directory for site-specific modules.
124
125    If 'prefix' is supplied, use it instead of sys.prefix or
126    sys.exec_prefix -- i.e., ignore 'plat_specific'.
127    """
128    if prefix is None:
129        prefix = plat_specific and EXEC_PREFIX or PREFIX
130
131    if os.name == "posix":
132        libpython = os.path.join(prefix,
133                                 "lib", "python" + get_python_version())
134        if standard_lib:
135            return libpython
136        else:
137            return os.path.join(libpython, "site-packages")
138
139    elif os.name == "nt":
140        if standard_lib:
141            return os.path.join(prefix, "Lib")
142        else:
143            if get_python_version() < "2.2":
144                return prefix
145            else:
146                return os.path.join(prefix, "Lib", "site-packages")
147
148    elif os.name == "os2":
149        if standard_lib:
150            return os.path.join(prefix, "Lib")
151        else:
152            return os.path.join(prefix, "Lib", "site-packages")
153
154    else:
155        raise DistutilsPlatformError(
156            "I don't know where Python installs its library "
157            "on platform '%s'" % os.name)
158
159
160
161def customize_compiler(compiler):
162    """Do any platform-specific customization of a CCompiler instance.
163
164    Mainly needed on Unix, so we can plug in the information that
165    varies across Unices and is stored in Python's Makefile.
166    """
167    if compiler.compiler_type == "unix":
168        if sys.platform == "darwin":
169            # Perform first-time customization of compiler-related
170            # config vars on OS X now that we know we need a compiler.
171            # This is primarily to support Pythons from binary
172            # installers.  The kind and paths to build tools on
173            # the user system may vary significantly from the system
174            # that Python itself was built on.  Also the user OS
175            # version and build tools may not support the same set
176            # of CPU architectures for universal builds.
177            global _config_vars
178            # Use get_config_var() to ensure _config_vars is initialized.
179            if not get_config_var('CUSTOMIZED_OSX_COMPILER'):
180                import _osx_support
181                _osx_support.customize_compiler(_config_vars)
182                _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
183
184        (cc, cxx, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \
185            get_config_vars('CC', 'CXX', 'CFLAGS',
186                            'CCSHARED', 'LDSHARED', 'SO', 'AR',
187                            'ARFLAGS')
188
189        if 'CC' in os.environ:
190            newcc = os.environ['CC']
191            if (sys.platform == 'darwin'
192                    and 'LDSHARED' not in os.environ
193                    and ldshared.startswith(cc)):
194                # On OS X, if CC is overridden, use that as the default
195                #       command for LDSHARED as well
196                ldshared = newcc + ldshared[len(cc):]
197            cc = newcc
198        if 'CXX' in os.environ:
199            cxx = os.environ['CXX']
200        if 'LDSHARED' in os.environ:
201            ldshared = os.environ['LDSHARED']
202        if 'CPP' in os.environ:
203            cpp = os.environ['CPP']
204        else:
205            cpp = cc + " -E"           # not always
206        if 'LDFLAGS' in os.environ:
207            ldshared = ldshared + ' ' + os.environ['LDFLAGS']
208        if 'CFLAGS' in os.environ:
209            cflags = cflags + ' ' + os.environ['CFLAGS']
210            ldshared = ldshared + ' ' + os.environ['CFLAGS']
211        if 'CPPFLAGS' in os.environ:
212            cpp = cpp + ' ' + os.environ['CPPFLAGS']
213            cflags = cflags + ' ' + os.environ['CPPFLAGS']
214            ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
215        if 'AR' in os.environ:
216            ar = os.environ['AR']
217        if 'ARFLAGS' in os.environ:
218            archiver = ar + ' ' + os.environ['ARFLAGS']
219        else:
220            archiver = ar + ' ' + ar_flags
221
222        cc_cmd = cc + ' ' + cflags
223        compiler.set_executables(
224            preprocessor=cpp,
225            compiler=cc_cmd,
226            compiler_so=cc_cmd + ' ' + ccshared,
227            compiler_cxx=cxx,
228            linker_so=ldshared,
229            linker_exe=cc,
230            archiver=archiver)
231
232        compiler.shared_lib_extension = so_ext
233
234
235def get_config_h_filename():
236    """Return full pathname of installed pyconfig.h file."""
237    if python_build:
238        if os.name == "nt":
239            inc_dir = os.path.join(project_base, "PC")
240        else:
241            inc_dir = project_base
242    else:
243        inc_dir = get_python_inc(plat_specific=1)
244    if get_python_version() < '2.2':
245        config_h = 'config.h'
246    else:
247        # The name of the config.h file changed in 2.2
248        config_h = 'pyconfig.h'
249    return os.path.join(inc_dir, config_h)
250
251
252def get_makefile_filename():
253    """Return full pathname of installed Makefile from the Python build."""
254    if python_build:
255        return os.path.join(project_base, "Makefile")
256    lib_dir = get_python_lib(plat_specific=1, standard_lib=1)
257    return os.path.join(lib_dir, "config", "Makefile")
258
259
260def parse_config_h(fp, g=None):
261    """Parse a config.h-style file.
262
263    A dictionary containing name/value pairs is returned.  If an
264    optional dictionary is passed in as the second argument, it is
265    used instead of a new dictionary.
266    """
267    if g is None:
268        g = {}
269    define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
270    undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
271    #
272    while 1:
273        line = fp.readline()
274        if not line:
275            break
276        m = define_rx.match(line)
277        if m:
278            n, v = m.group(1, 2)
279            try: v = int(v)
280            except ValueError: pass
281            g[n] = v
282        else:
283            m = undef_rx.match(line)
284            if m:
285                g[m.group(1)] = 0
286    return g
287
288
289# Regexes needed for parsing Makefile (and similar syntaxes,
290# like old-style Setup files).
291_variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
292_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
293_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
294
295def parse_makefile(fn, g=None):
296    """Parse a Makefile-style file.
297
298    A dictionary containing name/value pairs is returned.  If an
299    optional dictionary is passed in as the second argument, it is
300    used instead of a new dictionary.
301    """
302    from distutils.text_file import TextFile
303    fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1)
304
305    if g is None:
306        g = {}
307    done = {}
308    notdone = {}
309
310    while 1:
311        line = fp.readline()
312        if line is None:  # eof
313            break
314        m = _variable_rx.match(line)
315        if m:
316            n, v = m.group(1, 2)
317            v = v.strip()
318            # `$$' is a literal `$' in make
319            tmpv = v.replace('$$', '')
320
321            if "$" in tmpv:
322                notdone[n] = v
323            else:
324                try:
325                    v = int(v)
326                except ValueError:
327                    # insert literal `$'
328                    done[n] = v.replace('$$', '$')
329                else:
330                    done[n] = v
331
332    # do variable interpolation here
333    while notdone:
334        for name in notdone.keys():
335            value = notdone[name]
336            m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
337            if m:
338                n = m.group(1)
339                found = True
340                if n in done:
341                    item = str(done[n])
342                elif n in notdone:
343                    # get it on a subsequent round
344                    found = False
345                elif n in os.environ:
346                    # do it like make: fall back to environment
347                    item = os.environ[n]
348                else:
349                    done[n] = item = ""
350                if found:
351                    after = value[m.end():]
352                    value = value[:m.start()] + item + after
353                    if "$" in after:
354                        notdone[name] = value
355                    else:
356                        try: value = int(value)
357                        except ValueError:
358                            done[name] = value.strip()
359                        else:
360                            done[name] = value
361                        del notdone[name]
362            else:
363                # bogus variable reference; just drop it since we can't deal
364                del notdone[name]
365
366    fp.close()
367
368    # strip spurious spaces
369    for k, v in done.items():
370        if isinstance(v, str):
371            done[k] = v.strip()
372
373    # save the results in the global dictionary
374    g.update(done)
375    return g
376
377
378def expand_makefile_vars(s, vars):
379    """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
380    'string' according to 'vars' (a dictionary mapping variable names to
381    values).  Variables not present in 'vars' are silently expanded to the
382    empty string.  The variable values in 'vars' should not contain further
383    variable expansions; if 'vars' is the output of 'parse_makefile()',
384    you're fine.  Returns a variable-expanded version of 's'.
385    """
386
387    # This algorithm does multiple expansion, so if vars['foo'] contains
388    # "${bar}", it will expand ${foo} to ${bar}, and then expand
389    # ${bar}... and so forth.  This is fine as long as 'vars' comes from
390    # 'parse_makefile()', which takes care of such expansions eagerly,
391    # according to make's variable expansion semantics.
392
393    while 1:
394        m = _findvar1_rx.search(s) or _findvar2_rx.search(s)
395        if m:
396            (beg, end) = m.span()
397            s = s[0:beg] + vars.get(m.group(1)) + s[end:]
398        else:
399            break
400    return s
401
402
403_config_vars = None
404
405def _init_posix():
406    """Initialize the module as appropriate for POSIX systems."""
407    # _sysconfigdata is generated at build time, see the sysconfig module
408    from _sysconfigdata import build_time_vars
409    global _config_vars
410    _config_vars = {}
411    _config_vars.update(build_time_vars)
412
413
414def _init_nt():
415    """Initialize the module as appropriate for NT"""
416    g = {}
417    # set basic install directories
418    g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
419    g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
420
421    # XXX hmmm.. a normal install puts include files here
422    g['INCLUDEPY'] = get_python_inc(plat_specific=0)
423
424    g['SO'] = '.pyd'
425    g['EXE'] = ".exe"
426    g['VERSION'] = get_python_version().replace(".", "")
427    g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable))
428
429    global _config_vars
430    _config_vars = g
431
432
433def _init_os2():
434    """Initialize the module as appropriate for OS/2"""
435    g = {}
436    # set basic install directories
437    g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
438    g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
439
440    # XXX hmmm.. a normal install puts include files here
441    g['INCLUDEPY'] = get_python_inc(plat_specific=0)
442
443    g['SO'] = '.pyd'
444    g['EXE'] = ".exe"
445
446    global _config_vars
447    _config_vars = g
448
449
450def get_config_vars(*args):
451    """With no arguments, return a dictionary of all configuration
452    variables relevant for the current platform.  Generally this includes
453    everything needed to build extensions and install both pure modules and
454    extensions.  On Unix, this means every variable defined in Python's
455    installed Makefile; on Windows and Mac OS it's a much smaller set.
456
457    With arguments, return a list of values that result from looking up
458    each argument in the configuration variable dictionary.
459    """
460    global _config_vars
461    if _config_vars is None:
462        func = globals().get("_init_" + os.name)
463        if func:
464            func()
465        else:
466            _config_vars = {}
467
468        # Normalized versions of prefix and exec_prefix are handy to have;
469        # in fact, these are the standard versions used most places in the
470        # Distutils.
471        _config_vars['prefix'] = PREFIX
472        _config_vars['exec_prefix'] = EXEC_PREFIX
473
474        # OS X platforms require special customization to handle
475        # multi-architecture, multi-os-version installers
476        if sys.platform == 'darwin':
477            import _osx_support
478            _osx_support.customize_config_vars(_config_vars)
479
480    if args:
481        vals = []
482        for name in args:
483            vals.append(_config_vars.get(name))
484        return vals
485    else:
486        return _config_vars
487
488def get_config_var(name):
489    """Return the value of a single variable using the dictionary
490    returned by 'get_config_vars()'.  Equivalent to
491    get_config_vars().get(name)
492    """
493    return get_config_vars().get(name)
494