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