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    if sys.executable:
32        project_base = os.path.dirname(os.path.abspath(sys.executable))
33    else:
34        # sys.executable can be empty if argv[0] has been changed and Python is
35        # unable to retrieve the real program name
36        project_base = os.getcwd()
37
38
39# python_build: (Boolean) if true, we're either building Python or
40# building an extension with an un-installed Python, so we use
41# different (hard-wired) directories.
42def _is_python_source_dir(d):
43    for fn in ("Setup", "Setup.local"):
44        if os.path.isfile(os.path.join(d, "Modules", fn)):
45            return True
46    return False
47
48_sys_home = getattr(sys, '_home', None)
49
50if os.name == 'nt':
51    def _fix_pcbuild(d):
52        if d and os.path.normcase(d).startswith(
53                os.path.normcase(os.path.join(PREFIX, "PCbuild"))):
54            return PREFIX
55        return d
56    project_base = _fix_pcbuild(project_base)
57    _sys_home = _fix_pcbuild(_sys_home)
58
59def _python_build():
60    if _sys_home:
61        return _is_python_source_dir(_sys_home)
62    return _is_python_source_dir(project_base)
63
64python_build = _python_build()
65
66
67# Calculate the build qualifier flags if they are defined.  Adding the flags
68# to the include and lib directories only makes sense for an installation, not
69# an in-source build.
70build_flags = ''
71try:
72    if not python_build:
73        build_flags = sys.abiflags
74except AttributeError:
75    # It's not a configure-based build, so the sys module doesn't have
76    # this attribute, which is fine.
77    pass
78
79def get_python_version():
80    """Return a string containing the major and minor Python version,
81    leaving off the patchlevel.  Sample return values could be '1.5'
82    or '2.2'.
83    """
84    return '%d.%d' % sys.version_info[:2]
85
86
87def get_python_inc(plat_specific=0, prefix=None):
88    """Return the directory containing installed Python header files.
89
90    If 'plat_specific' is false (the default), this is the path to the
91    non-platform-specific header files, i.e. Python.h and so on;
92    otherwise, this is the path to platform-specific header files
93    (namely pyconfig.h).
94
95    If 'prefix' is supplied, use it instead of sys.base_prefix or
96    sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
97    """
98    if prefix is None:
99        prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
100    if os.name == "posix":
101        if python_build:
102            # Assume the executable is in the build directory.  The
103            # pyconfig.h file should be in the same directory.  Since
104            # the build directory may not be the source directory, we
105            # must use "srcdir" from the makefile to find the "Include"
106            # directory.
107            if plat_specific:
108                return _sys_home or project_base
109            else:
110                incdir = os.path.join(get_config_var('srcdir'), 'Include')
111                return os.path.normpath(incdir)
112        python_dir = 'python' + get_python_version() + build_flags
113        return os.path.join(prefix, "include", python_dir)
114    elif os.name == "nt":
115        if python_build:
116            # Include both the include and PC dir to ensure we can find
117            # pyconfig.h
118            return (os.path.join(prefix, "include") + os.path.pathsep +
119                    os.path.join(prefix, "PC"))
120        return os.path.join(prefix, "include")
121    else:
122        raise DistutilsPlatformError(
123            "I don't know where Python installs its C header files "
124            "on platform '%s'" % os.name)
125
126
127def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
128    """Return the directory containing the Python library (standard or
129    site additions).
130
131    If 'plat_specific' is true, return the directory containing
132    platform-specific modules, i.e. any module from a non-pure-Python
133    module distribution; otherwise, return the platform-shared library
134    directory.  If 'standard_lib' is true, return the directory
135    containing standard Python library modules; otherwise, return the
136    directory for site-specific modules.
137
138    If 'prefix' is supplied, use it instead of sys.base_prefix or
139    sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
140    """
141    if prefix is None:
142        if standard_lib:
143            prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
144        else:
145            prefix = plat_specific and EXEC_PREFIX or PREFIX
146
147    if os.name == "posix":
148        if plat_specific or standard_lib:
149            # Platform-specific modules (any module from a non-pure-Python
150            # module distribution) or standard Python library modules.
151            libdir = sys.platlibdir
152        else:
153            # Pure Python
154            libdir = "lib"
155        libpython = os.path.join(prefix, libdir,
156                                 "python" + get_python_version())
157        if standard_lib:
158            return libpython
159        else:
160            return os.path.join(libpython, "site-packages")
161    elif os.name == "nt":
162        if standard_lib:
163            return os.path.join(prefix, "Lib")
164        else:
165            return os.path.join(prefix, "Lib", "site-packages")
166    else:
167        raise DistutilsPlatformError(
168            "I don't know where Python installs its library "
169            "on platform '%s'" % os.name)
170
171
172
173def customize_compiler(compiler):
174    """Do any platform-specific customization of a CCompiler instance.
175
176    Mainly needed on Unix, so we can plug in the information that
177    varies across Unices and is stored in Python's Makefile.
178    """
179    if compiler.compiler_type == "unix":
180        if sys.platform == "darwin":
181            # Perform first-time customization of compiler-related
182            # config vars on OS X now that we know we need a compiler.
183            # This is primarily to support Pythons from binary
184            # installers.  The kind and paths to build tools on
185            # the user system may vary significantly from the system
186            # that Python itself was built on.  Also the user OS
187            # version and build tools may not support the same set
188            # of CPU architectures for universal builds.
189            global _config_vars
190            # Use get_config_var() to ensure _config_vars is initialized.
191            if not get_config_var('CUSTOMIZED_OSX_COMPILER'):
192                import _osx_support
193                _osx_support.customize_compiler(_config_vars)
194                _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
195
196        (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \
197            get_config_vars('CC', 'CXX', 'CFLAGS',
198                            'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS')
199
200        if 'CC' in os.environ:
201            newcc = os.environ['CC']
202            if (sys.platform == 'darwin'
203                    and 'LDSHARED' not in os.environ
204                    and ldshared.startswith(cc)):
205                # On OS X, if CC is overridden, use that as the default
206                #       command for LDSHARED as well
207                ldshared = newcc + ldshared[len(cc):]
208            cc = newcc
209        if 'CXX' in os.environ:
210            cxx = os.environ['CXX']
211        if 'LDSHARED' in os.environ:
212            ldshared = os.environ['LDSHARED']
213        if 'CPP' in os.environ:
214            cpp = os.environ['CPP']
215        else:
216            cpp = cc + " -E"           # not always
217        if 'LDFLAGS' in os.environ:
218            ldshared = ldshared + ' ' + os.environ['LDFLAGS']
219        if 'CFLAGS' in os.environ:
220            cflags = cflags + ' ' + os.environ['CFLAGS']
221            ldshared = ldshared + ' ' + os.environ['CFLAGS']
222        if 'CPPFLAGS' in os.environ:
223            cpp = cpp + ' ' + os.environ['CPPFLAGS']
224            cflags = cflags + ' ' + os.environ['CPPFLAGS']
225            ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
226        if 'AR' in os.environ:
227            ar = os.environ['AR']
228        if 'ARFLAGS' in os.environ:
229            archiver = ar + ' ' + os.environ['ARFLAGS']
230        else:
231            archiver = ar + ' ' + ar_flags
232
233        cc_cmd = cc + ' ' + cflags
234        compiler.set_executables(
235            preprocessor=cpp,
236            compiler=cc_cmd,
237            compiler_so=cc_cmd + ' ' + ccshared,
238            compiler_cxx=cxx,
239            linker_so=ldshared,
240            linker_exe=cc,
241            archiver=archiver)
242
243        compiler.shared_lib_extension = shlib_suffix
244
245
246def get_config_h_filename():
247    """Return full pathname of installed pyconfig.h file."""
248    if python_build:
249        if os.name == "nt":
250            inc_dir = os.path.join(_sys_home or project_base, "PC")
251        else:
252            inc_dir = _sys_home or project_base
253    else:
254        inc_dir = get_python_inc(plat_specific=1)
255
256    return os.path.join(inc_dir, 'pyconfig.h')
257
258
259def get_makefile_filename():
260    """Return full pathname of installed Makefile from the Python build."""
261    if python_build:
262        return os.path.join(_sys_home or project_base, "Makefile")
263    lib_dir = get_python_lib(plat_specific=0, standard_lib=1)
264    config_file = 'config-{}{}'.format(get_python_version(), build_flags)
265    if hasattr(sys.implementation, '_multiarch'):
266        config_file += '-%s' % sys.implementation._multiarch
267    return os.path.join(lib_dir, config_file, 'Makefile')
268
269
270def parse_config_h(fp, g=None):
271    """Parse a config.h-style file.
272
273    A dictionary containing name/value pairs is returned.  If an
274    optional dictionary is passed in as the second argument, it is
275    used instead of a new dictionary.
276    """
277    if g is None:
278        g = {}
279    define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
280    undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
281    #
282    while True:
283        line = fp.readline()
284        if not line:
285            break
286        m = define_rx.match(line)
287        if m:
288            n, v = m.group(1, 2)
289            try: v = int(v)
290            except ValueError: pass
291            g[n] = v
292        else:
293            m = undef_rx.match(line)
294            if m:
295                g[m.group(1)] = 0
296    return g
297
298
299# Regexes needed for parsing Makefile (and similar syntaxes,
300# like old-style Setup files).
301_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
302_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
303_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
304
305def parse_makefile(fn, g=None):
306    """Parse a Makefile-style file.
307
308    A dictionary containing name/value pairs is returned.  If an
309    optional dictionary is passed in as the second argument, it is
310    used instead of a new dictionary.
311    """
312    from distutils.text_file import TextFile
313    fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape")
314
315    if g is None:
316        g = {}
317    done = {}
318    notdone = {}
319
320    while True:
321        line = fp.readline()
322        if line is None: # eof
323            break
324        m = _variable_rx.match(line)
325        if m:
326            n, v = m.group(1, 2)
327            v = v.strip()
328            # `$$' is a literal `$' in make
329            tmpv = v.replace('$$', '')
330
331            if "$" in tmpv:
332                notdone[n] = v
333            else:
334                try:
335                    v = int(v)
336                except ValueError:
337                    # insert literal `$'
338                    done[n] = v.replace('$$', '$')
339                else:
340                    done[n] = v
341
342    # Variables with a 'PY_' prefix in the makefile. These need to
343    # be made available without that prefix through sysconfig.
344    # Special care is needed to ensure that variable expansion works, even
345    # if the expansion uses the name without a prefix.
346    renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
347
348    # do variable interpolation here
349    while notdone:
350        for name in list(notdone):
351            value = notdone[name]
352            m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
353            if m:
354                n = m.group(1)
355                found = True
356                if n in done:
357                    item = str(done[n])
358                elif n in notdone:
359                    # get it on a subsequent round
360                    found = False
361                elif n in os.environ:
362                    # do it like make: fall back to environment
363                    item = os.environ[n]
364
365                elif n in renamed_variables:
366                    if name.startswith('PY_') and name[3:] in renamed_variables:
367                        item = ""
368
369                    elif 'PY_' + n in notdone:
370                        found = False
371
372                    else:
373                        item = str(done['PY_' + n])
374                else:
375                    done[n] = item = ""
376                if found:
377                    after = value[m.end():]
378                    value = value[:m.start()] + item + after
379                    if "$" in after:
380                        notdone[name] = value
381                    else:
382                        try: value = int(value)
383                        except ValueError:
384                            done[name] = value.strip()
385                        else:
386                            done[name] = value
387                        del notdone[name]
388
389                        if name.startswith('PY_') \
390                            and name[3:] in renamed_variables:
391
392                            name = name[3:]
393                            if name not in done:
394                                done[name] = value
395            else:
396                # bogus variable reference; just drop it since we can't deal
397                del notdone[name]
398
399    fp.close()
400
401    # strip spurious spaces
402    for k, v in done.items():
403        if isinstance(v, str):
404            done[k] = v.strip()
405
406    # save the results in the global dictionary
407    g.update(done)
408    return g
409
410
411def expand_makefile_vars(s, vars):
412    """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
413    'string' according to 'vars' (a dictionary mapping variable names to
414    values).  Variables not present in 'vars' are silently expanded to the
415    empty string.  The variable values in 'vars' should not contain further
416    variable expansions; if 'vars' is the output of 'parse_makefile()',
417    you're fine.  Returns a variable-expanded version of 's'.
418    """
419
420    # This algorithm does multiple expansion, so if vars['foo'] contains
421    # "${bar}", it will expand ${foo} to ${bar}, and then expand
422    # ${bar}... and so forth.  This is fine as long as 'vars' comes from
423    # 'parse_makefile()', which takes care of such expansions eagerly,
424    # according to make's variable expansion semantics.
425
426    while True:
427        m = _findvar1_rx.search(s) or _findvar2_rx.search(s)
428        if m:
429            (beg, end) = m.span()
430            s = s[0:beg] + vars.get(m.group(1)) + s[end:]
431        else:
432            break
433    return s
434
435
436_config_vars = None
437
438def _init_posix():
439    """Initialize the module as appropriate for POSIX systems."""
440    # _sysconfigdata is generated at build time, see the sysconfig module
441    name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME',
442        '_sysconfigdata_{abi}_{platform}_{multiarch}'.format(
443        abi=sys.abiflags,
444        platform=sys.platform,
445        multiarch=getattr(sys.implementation, '_multiarch', ''),
446    ))
447    _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
448    build_time_vars = _temp.build_time_vars
449    global _config_vars
450    _config_vars = {}
451    _config_vars.update(build_time_vars)
452
453
454def _init_nt():
455    """Initialize the module as appropriate for NT"""
456    g = {}
457    # set basic install directories
458    g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
459    g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
460
461    # XXX hmmm.. a normal install puts include files here
462    g['INCLUDEPY'] = get_python_inc(plat_specific=0)
463
464    g['EXT_SUFFIX'] = _imp.extension_suffixes()[0]
465    g['EXE'] = ".exe"
466    g['VERSION'] = get_python_version().replace(".", "")
467    g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable))
468
469    global _config_vars
470    _config_vars = g
471
472
473def 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 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        # For backward compatibility, see issue19555
498        SO = _config_vars.get('EXT_SUFFIX')
499        if SO is not None:
500            _config_vars['SO'] = SO
501
502        # Always convert srcdir to an absolute path
503        srcdir = _config_vars.get('srcdir', project_base)
504        if os.name == 'posix':
505            if python_build:
506                # If srcdir is a relative path (typically '.' or '..')
507                # then it should be interpreted relative to the directory
508                # containing Makefile.
509                base = os.path.dirname(get_makefile_filename())
510                srcdir = os.path.join(base, srcdir)
511            else:
512                # srcdir is not meaningful since the installation is
513                # spread about the filesystem.  We choose the
514                # directory containing the Makefile since we know it
515                # exists.
516                srcdir = os.path.dirname(get_makefile_filename())
517        _config_vars['srcdir'] = os.path.abspath(os.path.normpath(srcdir))
518
519        # Convert srcdir into an absolute path if it appears necessary.
520        # Normally it is relative to the build directory.  However, during
521        # testing, for example, we might be running a non-installed python
522        # from a different directory.
523        if python_build and os.name == "posix":
524            base = project_base
525            if (not os.path.isabs(_config_vars['srcdir']) and
526                base != os.getcwd()):
527                # srcdir is relative and we are not in the same directory
528                # as the executable. Assume executable is in the build
529                # directory and make srcdir absolute.
530                srcdir = os.path.join(base, _config_vars['srcdir'])
531                _config_vars['srcdir'] = os.path.normpath(srcdir)
532
533        # OS X platforms require special customization to handle
534        # multi-architecture, multi-os-version installers
535        if sys.platform == 'darwin':
536            import _osx_support
537            _osx_support.customize_config_vars(_config_vars)
538
539    if args:
540        vals = []
541        for name in args:
542            vals.append(_config_vars.get(name))
543        return vals
544    else:
545        return _config_vars
546
547def get_config_var(name):
548    """Return the value of a single variable using the dictionary
549    returned by 'get_config_vars()'.  Equivalent to
550    get_config_vars().get(name)
551    """
552    if name == 'SO':
553        import warnings
554        warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2)
555    return get_config_vars().get(name)
556