• 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
12import _imp
13import os
14import re
15import sys
16
17from .errors import DistutilsPlatformError
18
19# These are needed in a couple of spots, so just compute them once.
20PREFIX = os.path.normpath(sys.prefix)
21EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
22BASE_PREFIX = os.path.normpath(sys.base_prefix)
23BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
24
25# Path to the base directory of the project. On Windows the binary may
26# live in project/PCBuild/win32 or project/PCBuild/amd64.
27# set for cross builds
28if "_PYTHON_PROJECT_BASE" in os.environ:
29    project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"])
30else:
31    project_base = os.path.dirname(os.path.abspath(sys.executable))
32if (os.name == 'nt' and
33    project_base.lower().endswith(('\\pcbuild\\win32', '\\pcbuild\\amd64'))):
34    project_base = os.path.dirname(os.path.dirname(project_base))
35
36# python_build: (Boolean) if true, we're either building Python or
37# building an extension with an un-installed Python, so we use
38# different (hard-wired) directories.
39# Setup.local is available for Makefile builds including VPATH builds,
40# Setup.dist is available on Windows
41def _is_python_source_dir(d):
42    for fn in ("Setup.dist", "Setup.local"):
43        if os.path.isfile(os.path.join(d, "Modules", fn)):
44            return True
45    return False
46_sys_home = getattr(sys, '_home', None)
47if (_sys_home and os.name == 'nt' and
48    _sys_home.lower().endswith(('\\pcbuild\\win32', '\\pcbuild\\amd64'))):
49    _sys_home = os.path.dirname(os.path.dirname(_sys_home))
50def _python_build():
51    if _sys_home:
52        return _is_python_source_dir(_sys_home)
53    return _is_python_source_dir(project_base)
54python_build = _python_build()
55
56# Calculate the build qualifier flags if they are defined.  Adding the flags
57# to the include and lib directories only makes sense for an installation, not
58# an in-source build.
59build_flags = ''
60try:
61    if not python_build:
62        build_flags = sys.abiflags
63except AttributeError:
64    # It's not a configure-based build, so the sys module doesn't have
65    # this attribute, which is fine.
66    pass
67
68def get_python_version():
69    """Return a string containing the major and minor Python version,
70    leaving off the patchlevel.  Sample return values could be '1.5'
71    or '2.2'.
72    """
73    return '%d.%d' % sys.version_info[:2]
74
75
76def get_python_inc(plat_specific=0, prefix=None):
77    """Return the directory containing installed Python header files.
78
79    If 'plat_specific' is false (the default), this is the path to the
80    non-platform-specific header files, i.e. Python.h and so on;
81    otherwise, this is the path to platform-specific header files
82    (namely pyconfig.h).
83
84    If 'prefix' is supplied, use it instead of sys.base_prefix or
85    sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
86    """
87    if prefix is None:
88        prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
89    if os.name == "posix":
90        if python_build:
91            # Assume the executable is in the build directory.  The
92            # pyconfig.h file should be in the same directory.  Since
93            # the build directory may not be the source directory, we
94            # must use "srcdir" from the makefile to find the "Include"
95            # directory.
96            base = _sys_home or project_base
97            if plat_specific:
98                return base
99            if _sys_home:
100                incdir = os.path.join(_sys_home, get_config_var('AST_H_DIR'))
101            else:
102                incdir = os.path.join(get_config_var('srcdir'), 'Include')
103            return os.path.normpath(incdir)
104        python_dir = 'python' + get_python_version() + build_flags
105        return os.path.join(prefix, "include", python_dir)
106    elif os.name == "nt":
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.base_prefix or
126    sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
127    """
128    if prefix is None:
129        if standard_lib:
130            prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
131        else:
132            prefix = plat_specific and EXEC_PREFIX or PREFIX
133
134    if os.name == "posix":
135        libpython = os.path.join(prefix,
136                                 "lib", "python" + get_python_version())
137        if standard_lib:
138            return libpython
139        else:
140            return os.path.join(libpython, "site-packages")
141    elif os.name == "nt":
142        if standard_lib:
143            return os.path.join(prefix, "Lib")
144        else:
145            return os.path.join(prefix, "Lib", "site-packages")
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    if compiler.compiler_type == "unix":
160        if sys.platform == "darwin":
161            # Perform first-time customization of compiler-related
162            # config vars on OS X now that we know we need a compiler.
163            # This is primarily to support Pythons from binary
164            # installers.  The kind and paths to build tools on
165            # the user system may vary significantly from the system
166            # that Python itself was built on.  Also the user OS
167            # version and build tools may not support the same set
168            # of CPU architectures for universal builds.
169            global _config_vars
170            # Use get_config_var() to ensure _config_vars is initialized.
171            if not get_config_var('CUSTOMIZED_OSX_COMPILER'):
172                import _osx_support
173                _osx_support.customize_compiler(_config_vars)
174                _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
175
176        (cc, cxx, opt, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \
177            get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
178                            'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS')
179
180        if 'CC' in os.environ:
181            newcc = os.environ['CC']
182            if (sys.platform == 'darwin'
183                    and 'LDSHARED' not in os.environ
184                    and ldshared.startswith(cc)):
185                # On OS X, if CC is overridden, use that as the default
186                #       command for LDSHARED as well
187                ldshared = newcc + ldshared[len(cc):]
188            cc = newcc
189        if 'CXX' in os.environ:
190            cxx = os.environ['CXX']
191        if 'LDSHARED' in os.environ:
192            ldshared = os.environ['LDSHARED']
193        if 'CPP' in os.environ:
194            cpp = os.environ['CPP']
195        else:
196            cpp = cc + " -E"           # not always
197        if 'LDFLAGS' in os.environ:
198            ldshared = ldshared + ' ' + os.environ['LDFLAGS']
199        if 'CFLAGS' in os.environ:
200            cflags = opt + ' ' + os.environ['CFLAGS']
201            ldshared = ldshared + ' ' + os.environ['CFLAGS']
202        if 'CPPFLAGS' in os.environ:
203            cpp = cpp + ' ' + os.environ['CPPFLAGS']
204            cflags = cflags + ' ' + os.environ['CPPFLAGS']
205            ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
206        if 'AR' in os.environ:
207            ar = os.environ['AR']
208        if 'ARFLAGS' in os.environ:
209            archiver = ar + ' ' + os.environ['ARFLAGS']
210        else:
211            archiver = ar + ' ' + ar_flags
212
213        cc_cmd = cc + ' ' + cflags
214        compiler.set_executables(
215            preprocessor=cpp,
216            compiler=cc_cmd,
217            compiler_so=cc_cmd + ' ' + ccshared,
218            compiler_cxx=cxx,
219            linker_so=ldshared,
220            linker_exe=cc,
221            archiver=archiver)
222
223        compiler.shared_lib_extension = shlib_suffix
224
225
226def get_config_h_filename():
227    """Return full pathname of installed pyconfig.h file."""
228    if python_build:
229        if os.name == "nt":
230            inc_dir = os.path.join(_sys_home or project_base, "PC")
231        else:
232            inc_dir = _sys_home or project_base
233    else:
234        inc_dir = get_python_inc(plat_specific=1)
235
236    return os.path.join(inc_dir, 'pyconfig.h')
237
238
239def get_makefile_filename():
240    """Return full pathname of installed Makefile from the Python build."""
241    if python_build:
242        return os.path.join(_sys_home or project_base, "Makefile")
243    lib_dir = get_python_lib(plat_specific=0, standard_lib=1)
244    config_file = 'config-{}{}'.format(get_python_version(), build_flags)
245    if hasattr(sys.implementation, '_multiarch'):
246        config_file += '-%s' % sys.implementation._multiarch
247    return os.path.join(lib_dir, config_file, '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 True:
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(r"([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, errors="surrogateescape")
294
295    if g is None:
296        g = {}
297    done = {}
298    notdone = {}
299
300    while True:
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    # Variables with a 'PY_' prefix in the makefile. These need to
323    # be made available without that prefix through sysconfig.
324    # Special care is needed to ensure that variable expansion works, even
325    # if the expansion uses the name without a prefix.
326    renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
327
328    # do variable interpolation here
329    while notdone:
330        for name in list(notdone):
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
345                elif n in renamed_variables:
346                    if name.startswith('PY_') and name[3:] in renamed_variables:
347                        item = ""
348
349                    elif 'PY_' + n in notdone:
350                        found = False
351
352                    else:
353                        item = str(done['PY_' + n])
354                else:
355                    done[n] = item = ""
356                if found:
357                    after = value[m.end():]
358                    value = value[:m.start()] + item + after
359                    if "$" in after:
360                        notdone[name] = value
361                    else:
362                        try: value = int(value)
363                        except ValueError:
364                            done[name] = value.strip()
365                        else:
366                            done[name] = value
367                        del notdone[name]
368
369                        if name.startswith('PY_') \
370                            and name[3:] in renamed_variables:
371
372                            name = name[3:]
373                            if name not in done:
374                                done[name] = value
375            else:
376                # bogus variable reference; just drop it since we can't deal
377                del notdone[name]
378
379    fp.close()
380
381    # strip spurious spaces
382    for k, v in done.items():
383        if isinstance(v, str):
384            done[k] = v.strip()
385
386    # save the results in the global dictionary
387    g.update(done)
388    return g
389
390
391def expand_makefile_vars(s, vars):
392    """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
393    'string' according to 'vars' (a dictionary mapping variable names to
394    values).  Variables not present in 'vars' are silently expanded to the
395    empty string.  The variable values in 'vars' should not contain further
396    variable expansions; if 'vars' is the output of 'parse_makefile()',
397    you're fine.  Returns a variable-expanded version of 's'.
398    """
399
400    # This algorithm does multiple expansion, so if vars['foo'] contains
401    # "${bar}", it will expand ${foo} to ${bar}, and then expand
402    # ${bar}... and so forth.  This is fine as long as 'vars' comes from
403    # 'parse_makefile()', which takes care of such expansions eagerly,
404    # according to make's variable expansion semantics.
405
406    while True:
407        m = _findvar1_rx.search(s) or _findvar2_rx.search(s)
408        if m:
409            (beg, end) = m.span()
410            s = s[0:beg] + vars.get(m.group(1)) + s[end:]
411        else:
412            break
413    return s
414
415
416_config_vars = None
417
418def _init_posix():
419    """Initialize the module as appropriate for POSIX systems."""
420    # _sysconfigdata is generated at build time, see the sysconfig module
421    name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME',
422        '_sysconfigdata_{abi}_{platform}_{multiarch}'.format(
423        abi=sys.abiflags,
424        platform=sys.platform,
425        multiarch=getattr(sys.implementation, '_multiarch', ''),
426    ))
427    _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
428    build_time_vars = _temp.build_time_vars
429    global _config_vars
430    _config_vars = {}
431    _config_vars.update(build_time_vars)
432
433
434def _init_nt():
435    """Initialize the module as appropriate for NT"""
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['EXT_SUFFIX'] = _imp.extension_suffixes()[0]
445    g['EXE'] = ".exe"
446    g['VERSION'] = get_python_version().replace(".", "")
447    g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable))
448
449    global _config_vars
450    _config_vars = g
451
452
453def get_config_vars(*args):
454    """With no arguments, return a dictionary of all configuration
455    variables relevant for the current platform.  Generally this includes
456    everything needed to build extensions and install both pure modules and
457    extensions.  On Unix, this means every variable defined in Python's
458    installed Makefile; on Windows it's a much smaller set.
459
460    With arguments, return a list of values that result from looking up
461    each argument in the configuration variable dictionary.
462    """
463    global _config_vars
464    if _config_vars is None:
465        func = globals().get("_init_" + os.name)
466        if func:
467            func()
468        else:
469            _config_vars = {}
470
471        # Normalized versions of prefix and exec_prefix are handy to have;
472        # in fact, these are the standard versions used most places in the
473        # Distutils.
474        _config_vars['prefix'] = PREFIX
475        _config_vars['exec_prefix'] = EXEC_PREFIX
476
477        # For backward compatibility, see issue19555
478        SO = _config_vars.get('EXT_SUFFIX')
479        if SO is not None:
480            _config_vars['SO'] = SO
481
482        # Always convert srcdir to an absolute path
483        srcdir = _config_vars.get('srcdir', project_base)
484        if os.name == 'posix':
485            if python_build:
486                # If srcdir is a relative path (typically '.' or '..')
487                # then it should be interpreted relative to the directory
488                # containing Makefile.
489                base = os.path.dirname(get_makefile_filename())
490                srcdir = os.path.join(base, srcdir)
491            else:
492                # srcdir is not meaningful since the installation is
493                # spread about the filesystem.  We choose the
494                # directory containing the Makefile since we know it
495                # exists.
496                srcdir = os.path.dirname(get_makefile_filename())
497        _config_vars['srcdir'] = os.path.abspath(os.path.normpath(srcdir))
498
499        # Convert srcdir into an absolute path if it appears necessary.
500        # Normally it is relative to the build directory.  However, during
501        # testing, for example, we might be running a non-installed python
502        # from a different directory.
503        if python_build and os.name == "posix":
504            base = project_base
505            if (not os.path.isabs(_config_vars['srcdir']) and
506                base != os.getcwd()):
507                # srcdir is relative and we are not in the same directory
508                # as the executable. Assume executable is in the build
509                # directory and make srcdir absolute.
510                srcdir = os.path.join(base, _config_vars['srcdir'])
511                _config_vars['srcdir'] = os.path.normpath(srcdir)
512
513        # OS X platforms require special customization to handle
514        # multi-architecture, multi-os-version installers
515        if sys.platform == 'darwin':
516            import _osx_support
517            _osx_support.customize_config_vars(_config_vars)
518
519    if args:
520        vals = []
521        for name in args:
522            vals.append(_config_vars.get(name))
523        return vals
524    else:
525        return _config_vars
526
527def get_config_var(name):
528    """Return the value of a single variable using the dictionary
529    returned by 'get_config_vars()'.  Equivalent to
530    get_config_vars().get(name)
531    """
532    if name == 'SO':
533        import warnings
534        warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2)
535    return get_config_vars().get(name)
536