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