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