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:        <[email protected]>
10"""
11
12import _imp
13import os
14import re
15import sys
16import warnings
17
18from functools import partial
19
20from .errors import DistutilsPlatformError
21
22from sysconfig import (
23    _PREFIX as PREFIX,
24    _BASE_PREFIX as BASE_PREFIX,
25    _EXEC_PREFIX as EXEC_PREFIX,
26    _BASE_EXEC_PREFIX as BASE_EXEC_PREFIX,
27    _PROJECT_BASE as project_base,
28    _PYTHON_BUILD as python_build,
29    _init_posix as sysconfig_init_posix,
30    parse_config_h as sysconfig_parse_config_h,
31
32    _init_non_posix,
33
34    _variable_rx,
35    _findvar1_rx,
36    _findvar2_rx,
37
38    expand_makefile_vars,
39    is_python_build,
40    get_config_h_filename,
41    get_config_var,
42    get_config_vars,
43    get_makefile_filename,
44    get_python_version,
45)
46
47# This is better than
48# from sysconfig import _CONFIG_VARS as _config_vars
49# because it makes sure that the global dictionary is initialized
50# which might not be true in the time of import.
51_config_vars = get_config_vars()
52
53warnings.warn(
54    'The distutils.sysconfig module is deprecated, use sysconfig instead',
55    DeprecationWarning,
56    stacklevel=2
57)
58
59
60# Following functions are the same as in sysconfig but with different API
61def parse_config_h(fp, g=None):
62    return sysconfig_parse_config_h(fp, vars=g)
63
64
65_python_build = partial(is_python_build, check_home=True)
66_init_posix = partial(sysconfig_init_posix, _config_vars)
67_init_nt = partial(_init_non_posix, _config_vars)
68
69
70# Similar function is also implemented in sysconfig as _parse_makefile
71# but without the parsing capabilities of distutils.text_file.TextFile.
72def parse_makefile(fn, g=None):
73    """Parse a Makefile-style file.
74    A dictionary containing name/value pairs is returned.  If an
75    optional dictionary is passed in as the second argument, it is
76    used instead of a new dictionary.
77    """
78    from distutils.text_file import TextFile
79    fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape")
80
81    if g is None:
82        g = {}
83    done = {}
84    notdone = {}
85
86    while True:
87        line = fp.readline()
88        if line is None: # eof
89            break
90        m = re.match(_variable_rx, line)
91        if m:
92            n, v = m.group(1, 2)
93            v = v.strip()
94            # `$$' is a literal `$' in make
95            tmpv = v.replace('$$', '')
96
97            if "$" in tmpv:
98                notdone[n] = v
99            else:
100                try:
101                    v = int(v)
102                except ValueError:
103                    # insert literal `$'
104                    done[n] = v.replace('$$', '$')
105                else:
106                    done[n] = v
107
108    # Variables with a 'PY_' prefix in the makefile. These need to
109    # be made available without that prefix through sysconfig.
110    # Special care is needed to ensure that variable expansion works, even
111    # if the expansion uses the name without a prefix.
112    renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
113
114    # do variable interpolation here
115    while notdone:
116        for name in list(notdone):
117            value = notdone[name]
118            m = re.search(_findvar1_rx, value) or re.search(_findvar2_rx, value)
119            if m:
120                n = m.group(1)
121                found = True
122                if n in done:
123                    item = str(done[n])
124                elif n in notdone:
125                    # get it on a subsequent round
126                    found = False
127                elif n in os.environ:
128                    # do it like make: fall back to environment
129                    item = os.environ[n]
130
131                elif n in renamed_variables:
132                    if name.startswith('PY_') and name[3:] in renamed_variables:
133                        item = ""
134
135                    elif 'PY_' + n in notdone:
136                        found = False
137
138                    else:
139                        item = str(done['PY_' + n])
140                else:
141                    done[n] = item = ""
142                if found:
143                    after = value[m.end():]
144                    value = value[:m.start()] + item + after
145                    if "$" in after:
146                        notdone[name] = value
147                    else:
148                        try: value = int(value)
149                        except ValueError:
150                            done[name] = value.strip()
151                        else:
152                            done[name] = value
153                        del notdone[name]
154
155                        if name.startswith('PY_') \
156                            and name[3:] in renamed_variables:
157
158                            name = name[3:]
159                            if name not in done:
160                                done[name] = value
161            else:
162                # bogus variable reference; just drop it since we can't deal
163                del notdone[name]
164
165    fp.close()
166
167    # strip spurious spaces
168    for k, v in done.items():
169        if isinstance(v, str):
170            done[k] = v.strip()
171
172    # save the results in the global dictionary
173    g.update(done)
174    return g
175
176
177# Following functions are deprecated together with this module and they
178# have no direct replacement
179
180# Calculate the build qualifier flags if they are defined.  Adding the flags
181# to the include and lib directories only makes sense for an installation, not
182# an in-source build.
183build_flags = ''
184try:
185    if not python_build:
186        build_flags = sys.abiflags
187except AttributeError:
188    # It's not a configure-based build, so the sys module doesn't have
189    # this attribute, which is fine.
190    pass
191
192
193def customize_compiler(compiler):
194    """Do any platform-specific customization of a CCompiler instance.
195
196    Mainly needed on Unix, so we can plug in the information that
197    varies across Unices and is stored in Python's Makefile.
198    """
199    if compiler.compiler_type == "unix":
200        if sys.platform == "darwin":
201            # Perform first-time customization of compiler-related
202            # config vars on OS X now that we know we need a compiler.
203            # This is primarily to support Pythons from binary
204            # installers.  The kind and paths to build tools on
205            # the user system may vary significantly from the system
206            # that Python itself was built on.  Also the user OS
207            # version and build tools may not support the same set
208            # of CPU architectures for universal builds.
209            if not _config_vars.get('CUSTOMIZED_OSX_COMPILER'):
210                import _osx_support
211                _osx_support.customize_compiler(_config_vars)
212                _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
213
214        (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \
215            get_config_vars('CC', 'CXX', 'CFLAGS',
216                            'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS')
217
218        if 'CC' in os.environ:
219            newcc = os.environ['CC']
220            if (sys.platform == 'darwin'
221                    and 'LDSHARED' not in os.environ
222                    and ldshared.startswith(cc)):
223                # On OS X, if CC is overridden, use that as the default
224                #       command for LDSHARED as well
225                ldshared = newcc + ldshared[len(cc):]
226            cc = newcc
227        if 'CXX' in os.environ:
228            cxx = os.environ['CXX']
229        if 'LDSHARED' in os.environ:
230            ldshared = os.environ['LDSHARED']
231        if 'CPP' in os.environ:
232            cpp = os.environ['CPP']
233        else:
234            cpp = cc + " -E"           # not always
235        if 'LDFLAGS' in os.environ:
236            ldshared = ldshared + ' ' + os.environ['LDFLAGS']
237        if 'CFLAGS' in os.environ:
238            cflags = cflags + ' ' + os.environ['CFLAGS']
239            ldshared = ldshared + ' ' + os.environ['CFLAGS']
240        if 'CPPFLAGS' in os.environ:
241            cpp = cpp + ' ' + os.environ['CPPFLAGS']
242            cflags = cflags + ' ' + os.environ['CPPFLAGS']
243            ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
244        if 'AR' in os.environ:
245            ar = os.environ['AR']
246        if 'ARFLAGS' in os.environ:
247            archiver = ar + ' ' + os.environ['ARFLAGS']
248        else:
249            archiver = ar + ' ' + ar_flags
250
251        cc_cmd = cc + ' ' + cflags
252        compiler.set_executables(
253            preprocessor=cpp,
254            compiler=cc_cmd,
255            compiler_so=cc_cmd + ' ' + ccshared,
256            compiler_cxx=cxx,
257            linker_so=ldshared,
258            linker_exe=cc,
259            archiver=archiver)
260
261        compiler.shared_lib_extension = shlib_suffix
262
263
264def get_python_inc(plat_specific=0, prefix=None):
265    """Return the directory containing installed Python header files.
266
267    If 'plat_specific' is false (the default), this is the path to the
268    non-platform-specific header files, i.e. Python.h and so on;
269    otherwise, this is the path to platform-specific header files
270    (namely pyconfig.h).
271
272    If 'prefix' is supplied, use it instead of sys.base_prefix or
273    sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
274    """
275    if prefix is None:
276        prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
277    if os.name == "posix":
278        if python_build:
279            # Assume the executable is in the build directory.  The
280            # pyconfig.h file should be in the same directory.  Since
281            # the build directory may not be the source directory, we
282            # must use "srcdir" from the makefile to find the "Include"
283            # directory.
284            if plat_specific:
285                return project_base
286            else:
287                incdir = os.path.join(get_config_var('srcdir'), 'Include')
288                return os.path.normpath(incdir)
289        python_dir = 'python' + get_python_version() + build_flags
290        return os.path.join(prefix, "include", python_dir)
291    elif os.name == "nt":
292        if python_build:
293            # Include both the include and PC dir to ensure we can find
294            # pyconfig.h
295            return (os.path.join(prefix, "include") + os.path.pathsep +
296                    os.path.join(prefix, "PC"))
297        return os.path.join(prefix, "include")
298    else:
299        raise DistutilsPlatformError(
300            "I don't know where Python installs its C header files "
301            "on platform '%s'" % os.name)
302
303
304def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
305    """Return the directory containing the Python library (standard or
306    site additions).
307
308    If 'plat_specific' is true, return the directory containing
309    platform-specific modules, i.e. any module from a non-pure-Python
310    module distribution; otherwise, return the platform-shared library
311    directory.  If 'standard_lib' is true, return the directory
312    containing standard Python library modules; otherwise, return the
313    directory for site-specific modules.
314
315    If 'prefix' is supplied, use it instead of sys.base_prefix or
316    sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
317    """
318    if prefix is None:
319        if standard_lib:
320            prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
321        else:
322            prefix = plat_specific and EXEC_PREFIX or PREFIX
323
324    if os.name == "posix":
325        if plat_specific or standard_lib:
326            # Platform-specific modules (any module from a non-pure-Python
327            # module distribution) or standard Python library modules.
328            libdir = sys.platlibdir
329        else:
330            # Pure Python
331            libdir = "lib"
332        libpython = os.path.join(prefix, libdir,
333                                 "python" + get_python_version())
334        if standard_lib:
335            return libpython
336        else:
337            return os.path.join(libpython, "site-packages")
338    elif os.name == "nt":
339        if standard_lib:
340            return os.path.join(prefix, "Lib")
341        else:
342            return os.path.join(prefix, "Lib", "site-packages")
343    else:
344        raise DistutilsPlatformError(
345            "I don't know where Python installs its library "
346            "on platform '%s'" % os.name)
347