blob: 7372e3eb4737cae082e10d5f370cb9a0a6bf6041 [file] [log] [blame]
Tarek Ziadéedacea32010-01-29 11:41:03 +00001"""Provide access to Python's configuration information.
2
3"""
4import sys
5import os
Florent Xiclunaa4707382010-03-11 00:05:17 +00006from os.path import pardir, realpath
Tarek Ziadéedacea32010-01-29 11:41:03 +00007
Tarek Ziadé16ed6cb2010-05-25 09:47:06 +00008__all__ = ['parse_config_h', 'get_config_h_filename', 'get_scheme_names',
9 'get_path_names', 'get_paths', 'get_path', 'get_config_vars',
10 'get_config_var', 'get_platform', 'get_python_version']
11
Tarek Ziadéedacea32010-01-29 11:41:03 +000012_INSTALL_SCHEMES = {
13 'posix_prefix': {
14 'stdlib': '{base}/lib/python{py_version_short}',
15 'platstdlib': '{platbase}/lib/python{py_version_short}',
16 'purelib': '{base}/lib/python{py_version_short}/site-packages',
17 'platlib': '{platbase}/lib/python{py_version_short}/site-packages',
18 'include': '{base}/include/python{py_version_short}',
19 'platinclude': '{platbase}/include/python{py_version_short}',
20 'scripts': '{base}/bin',
21 'data': '{base}',
22 },
23 'posix_home': {
24 'stdlib': '{base}/lib/python',
25 'platstdlib': '{base}/lib/python',
26 'purelib': '{base}/lib/python',
27 'platlib': '{base}/lib/python',
28 'include': '{base}/include/python',
29 'platinclude': '{base}/include/python',
30 'scripts': '{base}/bin',
31 'data' : '{base}',
32 },
33 'nt': {
34 'stdlib': '{base}/Lib',
35 'platstdlib': '{base}/Lib',
36 'purelib': '{base}/Lib/site-packages',
37 'platlib': '{base}/Lib/site-packages',
38 'include': '{base}/Include',
39 'platinclude': '{base}/Include',
40 'scripts': '{base}/Scripts',
41 'data' : '{base}',
42 },
43 'os2': {
44 'stdlib': '{base}/Lib',
45 'platstdlib': '{base}/Lib',
46 'purelib': '{base}/Lib/site-packages',
47 'platlib': '{base}/Lib/site-packages',
48 'include': '{base}/Include',
49 'platinclude': '{base}/Include',
50 'scripts': '{base}/Scripts',
51 'data' : '{base}',
52 },
53 'os2_home': {
Tarek Ziadé06710a82010-05-19 22:25:00 +000054 'stdlib': '{userbase}/lib/python{py_version_short}',
55 'platstdlib': '{userbase}/lib/python{py_version_short}',
56 'purelib': '{userbase}/lib/python{py_version_short}/site-packages',
57 'platlib': '{userbase}/lib/python{py_version_short}/site-packages',
Tarek Ziadéedacea32010-01-29 11:41:03 +000058 'include': '{userbase}/include/python{py_version_short}',
59 'scripts': '{userbase}/bin',
60 'data' : '{userbase}',
61 },
62 'nt_user': {
63 'stdlib': '{userbase}/Python{py_version_nodot}',
64 'platstdlib': '{userbase}/Python{py_version_nodot}',
65 'purelib': '{userbase}/Python{py_version_nodot}/site-packages',
66 'platlib': '{userbase}/Python{py_version_nodot}/site-packages',
67 'include': '{userbase}/Python{py_version_nodot}/Include',
68 'scripts': '{userbase}/Scripts',
69 'data' : '{userbase}',
70 },
71 'posix_user': {
Tarek Ziadé06710a82010-05-19 22:25:00 +000072 'stdlib': '{userbase}/lib/python{py_version_short}',
73 'platstdlib': '{userbase}/lib/python{py_version_short}',
74 'purelib': '{userbase}/lib/python{py_version_short}/site-packages',
75 'platlib': '{userbase}/lib/python{py_version_short}/site-packages',
Tarek Ziadéedacea32010-01-29 11:41:03 +000076 'include': '{userbase}/include/python{py_version_short}',
77 'scripts': '{userbase}/bin',
78 'data' : '{userbase}',
79 },
Ronald Oussoren4cda46a2010-05-08 10:49:43 +000080 'osx_framework_user': {
81 'stdlib': '{userbase}/lib/python',
82 'platstdlib': '{userbase}/lib/python',
83 'purelib': '{userbase}/lib/python/site-packages',
84 'platlib': '{userbase}/lib/python/site-packages',
85 'include': '{userbase}/include',
86 'scripts': '{userbase}/bin',
87 'data' : '{userbase}',
88 },
Tarek Ziadéedacea32010-01-29 11:41:03 +000089 }
90
91_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
92 'scripts', 'data')
93_PY_VERSION = sys.version.split()[0]
94_PY_VERSION_SHORT = sys.version[:3]
95_PY_VERSION_SHORT_NO_DOT = _PY_VERSION[0] + _PY_VERSION[2]
96_PREFIX = os.path.normpath(sys.prefix)
97_EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
98_CONFIG_VARS = None
99_USER_BASE = None
Victor Stinner171ba052010-03-12 14:20:59 +0000100if sys.executable:
101 _PROJECT_BASE = os.path.dirname(realpath(sys.executable))
102else:
103 # sys.executable can be empty if argv[0] has been changed and Python is
104 # unable to retrieve the real program name
105 _PROJECT_BASE = realpath(os.getcwd())
Tarek Ziadéedacea32010-01-29 11:41:03 +0000106
107if os.name == "nt" and "pcbuild" in _PROJECT_BASE[-8:].lower():
Florent Xiclunaa4707382010-03-11 00:05:17 +0000108 _PROJECT_BASE = realpath(os.path.join(_PROJECT_BASE, pardir))
Tarek Ziadéedacea32010-01-29 11:41:03 +0000109# PC/VS7.1
110if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower():
Florent Xiclunaa4707382010-03-11 00:05:17 +0000111 _PROJECT_BASE = realpath(os.path.join(_PROJECT_BASE, pardir, pardir))
Tarek Ziadéedacea32010-01-29 11:41:03 +0000112# PC/AMD64
113if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower():
Florent Xiclunaa4707382010-03-11 00:05:17 +0000114 _PROJECT_BASE = realpath(os.path.join(_PROJECT_BASE, pardir, pardir))
Tarek Ziadéedacea32010-01-29 11:41:03 +0000115
116def is_python_build():
117 for fn in ("Setup.dist", "Setup.local"):
118 if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)):
119 return True
120 return False
121
122_PYTHON_BUILD = is_python_build()
123
124if _PYTHON_BUILD:
125 for scheme in ('posix_prefix', 'posix_home'):
Vinay Sajip61c3f0d2010-09-20 10:13:13 +0000126 _INSTALL_SCHEMES[scheme]['include'] = '{projectbase}/Include'
Ronald Oussorene41a19e2010-06-15 16:05:20 +0000127 _INSTALL_SCHEMES[scheme]['platinclude'] = '{projectbase}/.'
Vinay Sajip61c3f0d2010-09-20 10:13:13 +0000128 _INSTALL_SCHEMES[scheme]['stdlib'] = '{projectbase}/Lib'
Tarek Ziadéedacea32010-01-29 11:41:03 +0000129
130def _subst_vars(s, local_vars):
131 try:
132 return s.format(**local_vars)
133 except KeyError:
134 try:
135 return s.format(**os.environ)
136 except KeyError as var:
137 raise AttributeError('{%s}' % var)
138
139def _extend_dict(target_dict, other_dict):
140 target_keys = target_dict.keys()
141 for key, value in other_dict.items():
142 if key in target_keys:
143 continue
144 target_dict[key] = value
145
146def _expand_vars(scheme, vars):
147 res = {}
148 if vars is None:
149 vars = {}
150 _extend_dict(vars, get_config_vars())
151
152 for key, value in _INSTALL_SCHEMES[scheme].items():
153 if os.name in ('posix', 'nt'):
154 value = os.path.expanduser(value)
155 res[key] = os.path.normpath(_subst_vars(value, vars))
156 return res
157
158def _get_default_scheme():
159 if os.name == 'posix':
160 # the default scheme for posix is posix_prefix
161 return 'posix_prefix'
162 return os.name
163
164def _getuserbase():
165 env_base = os.environ.get("PYTHONUSERBASE", None)
166 def joinuser(*args):
167 return os.path.expanduser(os.path.join(*args))
168
169 # what about 'os2emx', 'riscos' ?
170 if os.name == "nt":
171 base = os.environ.get("APPDATA") or "~"
172 return env_base if env_base else joinuser(base, "Python")
173
Ronald Oussoren4cda46a2010-05-08 10:49:43 +0000174 if sys.platform == "darwin":
175 framework = get_config_var("PYTHONFRAMEWORK")
176 if framework:
Ronald Oussorenbda46722010-08-01 09:02:50 +0000177 return env_base if env_base else joinuser("~", "Library", framework, "%d.%d"%(
Ronald Oussoren4cda46a2010-05-08 10:49:43 +0000178 sys.version_info[:2]))
179
Tarek Ziadéedacea32010-01-29 11:41:03 +0000180 return env_base if env_base else joinuser("~", ".local")
181
182
183def _parse_makefile(filename, vars=None):
184 """Parse a Makefile-style file.
185
186 A dictionary containing name/value pairs is returned. If an
187 optional dictionary is passed in as the second argument, it is
188 used instead of a new dictionary.
189 """
190 import re
191 # Regexes needed for parsing Makefile (and similar syntaxes,
192 # like old-style Setup files).
193 _variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
194 _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
195 _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
196
197 if vars is None:
198 vars = {}
199 done = {}
200 notdone = {}
201
202 with open(filename) as f:
203 lines = f.readlines()
204
205 for line in lines:
206 if line.startswith('#') or line.strip() == '':
207 continue
208 m = _variable_rx.match(line)
209 if m:
210 n, v = m.group(1, 2)
211 v = v.strip()
212 # `$$' is a literal `$' in make
213 tmpv = v.replace('$$', '')
214
215 if "$" in tmpv:
216 notdone[n] = v
217 else:
218 try:
219 v = int(v)
220 except ValueError:
221 # insert literal `$'
222 done[n] = v.replace('$$', '$')
223 else:
224 done[n] = v
225
226 # do variable interpolation here
227 variables = list(notdone.keys())
228
Ronald Oussorend21886c2010-07-20 16:07:10 +0000229 # Variables with a 'PY_' prefix in the makefile. These need to
230 # be made available without that prefix through sysconfig.
231 # Special care is needed to ensure that variable expansion works, even
232 # if the expansion uses the name without a prefix.
233 renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
234
Tarek Ziadéedacea32010-01-29 11:41:03 +0000235 while len(variables) > 0:
236 for name in tuple(variables):
237 value = notdone[name]
238 m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
239 if m is not None:
240 n = m.group(1)
241 found = True
242 if n in done:
243 item = str(done[n])
244 elif n in notdone:
245 # get it on a subsequent round
246 found = False
247 elif n in os.environ:
248 # do it like make: fall back to environment
249 item = os.environ[n]
Ronald Oussorend21886c2010-07-20 16:07:10 +0000250
251 elif n in renamed_variables:
252 if name.startswith('PY_') and name[3:] in renamed_variables:
253 item = ""
254
255 elif 'PY_' + n in notdone:
256 found = False
257
258 else:
259 item = str(done['PY_' + n])
260
Tarek Ziadéedacea32010-01-29 11:41:03 +0000261 else:
262 done[n] = item = ""
Ronald Oussorend21886c2010-07-20 16:07:10 +0000263
Tarek Ziadéedacea32010-01-29 11:41:03 +0000264 if found:
265 after = value[m.end():]
266 value = value[:m.start()] + item + after
267 if "$" in after:
268 notdone[name] = value
269 else:
270 try:
271 value = int(value)
272 except ValueError:
273 done[name] = value.strip()
274 else:
275 done[name] = value
276 variables.remove(name)
Ronald Oussorend21886c2010-07-20 16:07:10 +0000277
278 if name.startswith('PY_') \
279 and name[3:] in renamed_variables:
280
281 name = name[3:]
282 if name not in done:
283 done[name] = value
284
285
Tarek Ziadéedacea32010-01-29 11:41:03 +0000286 else:
287 # bogus variable reference; just drop it since we can't deal
288 variables.remove(name)
289
290 # save the results in the global dictionary
291 vars.update(done)
292 return vars
293
Tarek Ziadéedacea32010-01-29 11:41:03 +0000294
295def _get_makefile_filename():
296 if _PYTHON_BUILD:
297 return os.path.join(_PROJECT_BASE, "Makefile")
298 return os.path.join(get_path('stdlib'), "config", "Makefile")
299
Tarek Ziadéedacea32010-01-29 11:41:03 +0000300
301def _init_posix(vars):
302 """Initialize the module as appropriate for POSIX systems."""
303 # load the installed Makefile:
304 makefile = _get_makefile_filename()
305 try:
306 _parse_makefile(makefile, vars)
307 except IOError as e:
308 msg = "invalid Python installation: unable to open %s" % makefile
309 if hasattr(e, "strerror"):
310 msg = msg + " (%s)" % e.strerror
311 raise IOError(msg)
312 # load the installed pyconfig.h:
313 config_h = get_config_h_filename()
314 try:
315 parse_config_h(open(config_h), vars)
316 except IOError as e:
317 msg = "invalid Python installation: unable to open %s" % config_h
318 if hasattr(e, "strerror"):
319 msg = msg + " (%s)" % e.strerror
320 raise IOError(msg)
321 # On MacOSX we need to check the setting of the environment variable
322 # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so
323 # it needs to be compatible.
324 # If it isn't set we set it to the configure-time value
325 if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in vars:
326 cfg_target = vars['MACOSX_DEPLOYMENT_TARGET']
327 cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '')
328 if cur_target == '':
329 cur_target = cfg_target
330 os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target)
331 elif (list(map(int, cfg_target.split('.'))) >
332 list(map(int, cur_target.split('.')))):
333 msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" '
334 'during configure' % (cur_target, cfg_target))
335 raise IOError(msg)
336 # On AIX, there are wrong paths to the linker scripts in the Makefile
337 # -- these paths are relative to the Python source, but when installed
338 # the scripts are in another directory.
339 if _PYTHON_BUILD:
340 vars['LDSHARED'] = vars['BLDSHARED']
341
342def _init_non_posix(vars):
343 """Initialize the module as appropriate for NT"""
344 # set basic install directories
345 vars['LIBDEST'] = get_path('stdlib')
346 vars['BINLIBDEST'] = get_path('platstdlib')
347 vars['INCLUDEPY'] = get_path('include')
348 vars['SO'] = '.pyd'
349 vars['EXE'] = '.exe'
350 vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT
Florent Xicluna1d1ab972010-03-11 01:53:10 +0000351 vars['BINDIR'] = os.path.dirname(realpath(sys.executable))
Tarek Ziadéedacea32010-01-29 11:41:03 +0000352
353#
354# public APIs
355#
356
Tarek Ziadébd797682010-02-02 23:16:13 +0000357
358def parse_config_h(fp, vars=None):
359 """Parse a config.h-style file.
360
361 A dictionary containing name/value pairs is returned. If an
362 optional dictionary is passed in as the second argument, it is
363 used instead of a new dictionary.
364 """
365 import re
366 if vars is None:
367 vars = {}
368 define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
369 undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
370
371 while True:
372 line = fp.readline()
373 if not line:
374 break
375 m = define_rx.match(line)
376 if m:
377 n, v = m.group(1, 2)
378 try: v = int(v)
379 except ValueError: pass
380 vars[n] = v
381 else:
382 m = undef_rx.match(line)
383 if m:
384 vars[m.group(1)] = 0
385 return vars
386
387def get_config_h_filename():
388 """Returns the path of pyconfig.h."""
389 if _PYTHON_BUILD:
390 if os.name == "nt":
391 inc_dir = os.path.join(_PROJECT_BASE, "PC")
392 else:
393 inc_dir = _PROJECT_BASE
394 else:
395 inc_dir = get_path('platinclude')
396 return os.path.join(inc_dir, 'pyconfig.h')
397
Tarek Ziadéedacea32010-01-29 11:41:03 +0000398def get_scheme_names():
Tarek Ziadébd797682010-02-02 23:16:13 +0000399 """Returns a tuple containing the schemes names."""
400 schemes = list(_INSTALL_SCHEMES.keys())
401 schemes.sort()
402 return tuple(schemes)
Tarek Ziadéedacea32010-01-29 11:41:03 +0000403
404def get_path_names():
Tarek Ziadébd797682010-02-02 23:16:13 +0000405 """Returns a tuple containing the paths names."""
Tarek Ziadéedacea32010-01-29 11:41:03 +0000406 return _SCHEME_KEYS
407
408def get_paths(scheme=_get_default_scheme(), vars=None, expand=True):
409 """Returns a mapping containing an install scheme.
410
411 ``scheme`` is the install scheme name. If not provided, it will
412 return the default scheme for the current platform.
413 """
414 if expand:
415 return _expand_vars(scheme, vars)
416 else:
417 return _INSTALL_SCHEMES[scheme]
418
419def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True):
420 """Returns a path corresponding to the scheme.
421
422 ``scheme`` is the install scheme name.
423 """
424 return get_paths(scheme, vars, expand)[name]
425
426def get_config_vars(*args):
427 """With no arguments, return a dictionary of all configuration
428 variables relevant for the current platform.
429
430 On Unix, this means every variable defined in Python's installed Makefile;
431 On Windows and Mac OS it's a much smaller set.
432
433 With arguments, return a list of values that result from looking up
434 each argument in the configuration variable dictionary.
435 """
436 import re
437 global _CONFIG_VARS
438 if _CONFIG_VARS is None:
439 _CONFIG_VARS = {}
440 # Normalized versions of prefix and exec_prefix are handy to have;
441 # in fact, these are the standard versions used most places in the
442 # Distutils.
443 _CONFIG_VARS['prefix'] = _PREFIX
444 _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX
445 _CONFIG_VARS['py_version'] = _PY_VERSION
446 _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT
447 _CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2]
448 _CONFIG_VARS['base'] = _PREFIX
449 _CONFIG_VARS['platbase'] = _EXEC_PREFIX
Tarek Ziadéedacea32010-01-29 11:41:03 +0000450 _CONFIG_VARS['projectbase'] = _PROJECT_BASE
451
452 if os.name in ('nt', 'os2'):
453 _init_non_posix(_CONFIG_VARS)
454 if os.name == 'posix':
455 _init_posix(_CONFIG_VARS)
Ronald Oussoren4cda46a2010-05-08 10:49:43 +0000456 # Setting 'userbase' is done below the call to the
457 # init function to enable using 'get_config_var' in
458 # the init-function.
459 _CONFIG_VARS['userbase'] = _getuserbase()
460
Tarek Ziadéedacea32010-01-29 11:41:03 +0000461 if 'srcdir' not in _CONFIG_VARS:
462 _CONFIG_VARS['srcdir'] = _PROJECT_BASE
Ronald Oussorenab4fd612010-06-15 21:19:50 +0000463 else:
464 _CONFIG_VARS['srcdir'] = realpath(_CONFIG_VARS['srcdir'])
Tarek Ziadéedacea32010-01-29 11:41:03 +0000465
466
467 # Convert srcdir into an absolute path if it appears necessary.
468 # Normally it is relative to the build directory. However, during
469 # testing, for example, we might be running a non-installed python
470 # from a different directory.
471 if _PYTHON_BUILD and os.name == "posix":
472 base = _PROJECT_BASE
473 if (not os.path.isabs(_CONFIG_VARS['srcdir']) and
474 base != os.getcwd()):
475 # srcdir is relative and we are not in the same directory
476 # as the executable. Assume executable is in the build
477 # directory and make srcdir absolute.
478 srcdir = os.path.join(base, _CONFIG_VARS['srcdir'])
479 _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir)
480
481 if sys.platform == 'darwin':
482 kernel_version = os.uname()[2] # Kernel version (8.4.3)
483 major_version = int(kernel_version.split('.')[0])
484
485 if major_version < 8:
486 # On Mac OS X before 10.4, check if -arch and -isysroot
487 # are in CFLAGS or LDFLAGS and remove them if they are.
488 # This is needed when building extensions on a 10.3 system
489 # using a universal build of python.
490 for key in ('LDFLAGS', 'BASECFLAGS',
491 # a number of derived variables. These need to be
492 # patched up as well.
493 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
494 flags = _CONFIG_VARS[key]
495 flags = re.sub('-arch\s+\w+\s', ' ', flags)
496 flags = re.sub('-isysroot [^ \t]*', ' ', flags)
497 _CONFIG_VARS[key] = flags
498 else:
499 # Allow the user to override the architecture flags using
500 # an environment variable.
501 # NOTE: This name was introduced by Apple in OSX 10.5 and
502 # is used by several scripting languages distributed with
503 # that OS release.
504 if 'ARCHFLAGS' in os.environ:
505 arch = os.environ['ARCHFLAGS']
506 for key in ('LDFLAGS', 'BASECFLAGS',
507 # a number of derived variables. These need to be
508 # patched up as well.
509 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
510
511 flags = _CONFIG_VARS[key]
512 flags = re.sub('-arch\s+\w+\s', ' ', flags)
513 flags = flags + ' ' + arch
514 _CONFIG_VARS[key] = flags
515
516 # If we're on OSX 10.5 or later and the user tries to
517 # compiles an extension using an SDK that is not present
518 # on the current machine it is better to not use an SDK
519 # than to fail.
520 #
521 # The major usecase for this is users using a Python.org
522 # binary installer on OSX 10.6: that installer uses
523 # the 10.4u SDK, but that SDK is not installed by default
524 # when you install Xcode.
525 #
526 CFLAGS = _CONFIG_VARS.get('CFLAGS', '')
527 m = re.search('-isysroot\s+(\S+)', CFLAGS)
528 if m is not None:
529 sdk = m.group(1)
530 if not os.path.exists(sdk):
531 for key in ('LDFLAGS', 'BASECFLAGS',
532 # a number of derived variables. These need to be
533 # patched up as well.
534 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
535
536 flags = _CONFIG_VARS[key]
537 flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags)
538 _CONFIG_VARS[key] = flags
539
540 if args:
541 vals = []
542 for name in args:
543 vals.append(_CONFIG_VARS.get(name))
544 return vals
545 else:
546 return _CONFIG_VARS
547
548def get_config_var(name):
549 """Return the value of a single variable using the dictionary returned by
550 'get_config_vars()'.
551
552 Equivalent to get_config_vars().get(name)
553 """
554 return get_config_vars().get(name)
555
556def get_platform():
557 """Return a string that identifies the current platform.
558
559 This is used mainly to distinguish platform-specific build directories and
560 platform-specific built distributions. Typically includes the OS name
561 and version and the architecture (as supplied by 'os.uname()'),
562 although the exact information included depends on the OS; eg. for IRIX
563 the architecture isn't particularly important (IRIX only runs on SGI
564 hardware), but for Linux the kernel version isn't particularly
565 important.
566
567 Examples of returned values:
568 linux-i586
569 linux-alpha (?)
570 solaris-2.6-sun4u
571 irix-5.3
572 irix64-6.2
573
574 Windows will return one of:
575 win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
576 win-ia64 (64bit Windows on Itanium)
577 win32 (all others - specifically, sys.platform is returned)
578
579 For other non-POSIX platforms, currently just returns 'sys.platform'.
580 """
581 import re
582 if os.name == 'nt':
583 # sniff sys.version for architecture.
584 prefix = " bit ("
585 i = sys.version.find(prefix)
586 if i == -1:
587 return sys.platform
588 j = sys.version.find(")", i)
589 look = sys.version[i+len(prefix):j].lower()
590 if look == 'amd64':
591 return 'win-amd64'
592 if look == 'itanium':
593 return 'win-ia64'
594 return sys.platform
595
596 if os.name != "posix" or not hasattr(os, 'uname'):
597 # XXX what about the architecture? NT is Intel or Alpha,
598 # Mac OS is M68k or PPC, etc.
599 return sys.platform
600
601 # Try to distinguish various flavours of Unix
602 osname, host, release, version, machine = os.uname()
603
604 # Convert the OS name to lowercase, remove '/' characters
605 # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh")
606 osname = osname.lower().replace('/', '')
607 machine = machine.replace(' ', '_')
608 machine = machine.replace('/', '-')
609
610 if osname[:5] == "linux":
611 # At least on Linux/Intel, 'machine' is the processor --
612 # i386, etc.
613 # XXX what about Alpha, SPARC, etc?
614 return "%s-%s" % (osname, machine)
615 elif osname[:5] == "sunos":
616 if release[0] >= "5": # SunOS 5 == Solaris 2
617 osname = "solaris"
618 release = "%d.%s" % (int(release[0]) - 3, release[2:])
619 # fall through to standard osname-release-machine representation
620 elif osname[:4] == "irix": # could be "irix64"!
621 return "%s-%s" % (osname, release)
622 elif osname[:3] == "aix":
623 return "%s-%s.%s" % (osname, version, release)
624 elif osname[:6] == "cygwin":
625 osname = "cygwin"
626 rel_re = re.compile (r'[\d.]+')
627 m = rel_re.match(release)
628 if m:
629 release = m.group()
630 elif osname[:6] == "darwin":
631 #
632 # For our purposes, we'll assume that the system version from
633 # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set
634 # to. This makes the compatibility story a bit more sane because the
635 # machine is going to compile and link as if it were
636 # MACOSX_DEPLOYMENT_TARGET.
637 cfgvars = get_config_vars()
638 macver = os.environ.get('MACOSX_DEPLOYMENT_TARGET')
639 if not macver:
640 macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET')
641
642 if 1:
643 # Always calculate the release of the running machine,
644 # needed to determine if we can build fat binaries or not.
645
646 macrelease = macver
647 # Get the system version. Reading this plist is a documented
648 # way to get the system version (see the documentation for
649 # the Gestalt Manager)
650 try:
651 f = open('/System/Library/CoreServices/SystemVersion.plist')
652 except IOError:
653 # We're on a plain darwin box, fall back to the default
654 # behaviour.
655 pass
656 else:
657 m = re.search(
658 r'<key>ProductUserVisibleVersion</key>\s*' +
659 r'<string>(.*?)</string>', f.read())
660 f.close()
661 if m is not None:
662 macrelease = '.'.join(m.group(1).split('.')[:2])
663 # else: fall back to the default behaviour
664
665 if not macver:
666 macver = macrelease
667
668 if macver:
669 release = macver
670 osname = "macosx"
671
672 if (macrelease + '.') >= '10.4.' and \
673 '-arch' in get_config_vars().get('CFLAGS', '').strip():
674 # The universal build will build fat binaries, but not on
675 # systems before 10.4
676 #
677 # Try to detect 4-way universal builds, those have machine-type
678 # 'universal' instead of 'fat'.
679
680 machine = 'fat'
681 cflags = get_config_vars().get('CFLAGS')
682
683 archs = re.findall('-arch\s+(\S+)', cflags)
Ronald Oussorend3950522010-07-11 09:05:07 +0000684 archs = tuple(sorted(set(archs)))
Tarek Ziadéedacea32010-01-29 11:41:03 +0000685
686 if len(archs) == 1:
687 machine = archs[0]
688 elif archs == ('i386', 'ppc'):
689 machine = 'fat'
690 elif archs == ('i386', 'x86_64'):
691 machine = 'intel'
692 elif archs == ('i386', 'ppc', 'x86_64'):
693 machine = 'fat3'
694 elif archs == ('ppc64', 'x86_64'):
695 machine = 'fat64'
696 elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'):
697 machine = 'universal'
698 else:
699 raise ValueError(
700 "Don't know machine value for archs=%r"%(archs,))
701
702 elif machine == 'i386':
703 # On OSX the machine type returned by uname is always the
704 # 32-bit variant, even if the executable architecture is
705 # the 64-bit variant
706 if sys.maxsize >= 2**32:
707 machine = 'x86_64'
708
709 elif machine in ('PowerPC', 'Power_Macintosh'):
710 # Pick a sane name for the PPC architecture.
711 # See 'i386' case
712 if sys.maxsize >= 2**32:
713 machine = 'ppc64'
714 else:
715 machine = 'ppc'
716
717 return "%s-%s-%s" % (osname, release, machine)
718
719
720def get_python_version():
721 return _PY_VERSION_SHORT
Tarek Ziadéa7514992010-05-25 09:44:36 +0000722
723def _print_dict(title, data):
724 for index, (key, value) in enumerate(sorted(data.items())):
725 if index == 0:
726 print('{0}: '.format(title))
727 print('\t{0} = "{1}"'.format(key, value))
728
729def _main():
730 """Displays all information sysconfig detains."""
731 print('Platform: "{0}"'.format(get_platform()))
732 print('Python version: "{0}"'.format(get_python_version()))
733 print('Current installation scheme: "{0}"'.format(_get_default_scheme()))
734 print('')
735 _print_dict('Paths', get_paths())
736 print('')
737 _print_dict('Variables', get_config_vars())
738
739if __name__ == '__main__':
740 _main()