blob: 3414a761e76b996db296033c4e8faba62b51ca19 [file] [log] [blame]
Jingwen Chen475b3cc2021-01-05 21:45:16 -05001"""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
Dan Willemsen52aefcd2022-03-24 21:56:24 -070016import warnings
17
18from functools import partial
Jingwen Chen475b3cc2021-01-05 21:45:16 -050019
20from .errors import DistutilsPlatformError
Jingwen Chen475b3cc2021-01-05 21:45:16 -050021
Dan Willemsen52aefcd2022-03-24 21:56:24 -070022from 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,
Jingwen Chen475b3cc2021-01-05 21:45:16 -050031
Dan Willemsen52aefcd2022-03-24 21:56:24 -070032 _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)
Jingwen Chen475b3cc2021-01-05 21:45:16 -050063
64
Dan Willemsen52aefcd2022-03-24 21:56:24 -070065# 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)
Jingwen Chen475b3cc2021-01-05 21:45:16 -050068
Jingwen Chen475b3cc2021-01-05 21:45:16 -050069
Dan Willemsen52aefcd2022-03-24 21:56:24 -070070_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)
Jingwen Chen475b3cc2021-01-05 21:45:16 -050073
Jingwen Chen475b3cc2021-01-05 21:45:16 -050074
Dan Willemsen52aefcd2022-03-24 21:56:24 -070075# 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")
Jingwen Chen475b3cc2021-01-05 21:45:16 -050085
Dan Willemsen52aefcd2022-03-24 21:56:24 -070086 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
Jingwen Chen475b3cc2021-01-05 21:45:16 -0500184
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
Dan Willemsen52aefcd2022-03-24 21:56:24 -0700197
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.
Jingwen Chen475b3cc2021-01-05 21:45:16 -0500203 """
Dan Willemsen52aefcd2022-03-24 21:56:24 -0700204 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
Jingwen Chen475b3cc2021-01-05 21:45:16 -0500267
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":
Elliott Hughes96c2b6b2021-01-26 11:15:15 -0800330 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())
Jingwen Chen475b3cc2021-01-05 21:45:16 -0500339 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)