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