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