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