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