Skip to content

Commit fc20d77

Browse files
committed
Issue #13590: OS X Xcode 4 - improve support for universal extension modules
In particular, fix extension module build failures when trying to use 32-bit-only installer Pythons on systems with Xcode 4 (currently OS X 10.8, 10.7, and optionally 10.6). * Backport 3.3.0 fixes to 3.2 branch (for release in 3.2.4) * Since Xcode 4 removes ppc support, extension module builds now check for ppc compiler support and by default remove ppc and ppc64 archs when they are not available. * Extension module builds now revert to using system installed headers and libs (/usr and /System/Library) if the SDK used to build the interpreter is not installed or has moved. * Try to avoid building extension modules with deprecated and problematic Apple llvm-gcc compiler. If original compiler is not available, use clang instead by default.
1 parent 6debd76 commit fc20d77

File tree

10 files changed

+862
-369
lines changed

10 files changed

+862
-369
lines changed

Lib/_osx_support.py

Lines changed: 488 additions & 0 deletions
Large diffs are not rendered by default.

Lib/distutils/sysconfig.py

Lines changed: 21 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
146146
"I don't know where Python installs its library "
147147
"on platform '%s'" % os.name)
148148

149-
_USE_CLANG = None
149+
150150

151151
def customize_compiler(compiler):
152152
"""Do any platform-specific customization of a CCompiler instance.
@@ -155,42 +155,28 @@ def customize_compiler(compiler):
155155
varies across Unices and is stored in Python's Makefile.
156156
"""
157157
if compiler.compiler_type == "unix":
158+
if sys.platform == "darwin":
159+
# Perform first-time customization of compiler-related
160+
# config vars on OS X now that we know we need a compiler.
161+
# This is primarily to support Pythons from binary
162+
# installers. The kind and paths to build tools on
163+
# the user system may vary significantly from the system
164+
# that Python itself was built on. Also the user OS
165+
# version and build tools may not support the same set
166+
# of CPU architectures for universal builds.
167+
global _config_vars
168+
if not _config_vars.get('CUSTOMIZED_OSX_COMPILER', ''):
169+
import _osx_support
170+
_osx_support.customize_compiler(_config_vars)
171+
_config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
172+
158173
(cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \
159174
get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
160175
'CCSHARED', 'LDSHARED', 'SO', 'AR', 'ARFLAGS')
161176

162177
newcc = None
163178
if 'CC' in os.environ:
164-
newcc = os.environ['CC']
165-
elif sys.platform == 'darwin' and cc == 'gcc-4.2':
166-
# Issue #13590:
167-
# Since Apple removed gcc-4.2 in Xcode 4.2, we can no
168-
# longer assume it is available for extension module builds.
169-
# If Python was built with gcc-4.2, check first to see if
170-
# it is available on this system; if not, try to use clang
171-
# instead unless the caller explicitly set CC.
172-
global _USE_CLANG
173-
if _USE_CLANG is None:
174-
from distutils import log
175-
from subprocess import Popen, PIPE
176-
p = Popen("! type gcc-4.2 && type clang && exit 2",
177-
shell=True, stdout=PIPE, stderr=PIPE)
178-
p.wait()
179-
if p.returncode == 2:
180-
_USE_CLANG = True
181-
log.warn("gcc-4.2 not found, using clang instead")
182-
else:
183-
_USE_CLANG = False
184-
if _USE_CLANG:
185-
newcc = 'clang'
186-
if newcc:
187-
# On OS X, if CC is overridden, use that as the default
188-
# command for LDSHARED as well
189-
if (sys.platform == 'darwin'
190-
and 'LDSHARED' not in os.environ
191-
and ldshared.startswith(cc)):
192-
ldshared = newcc + ldshared[len(cc):]
193-
cc = newcc
179+
cc = os.environ['CC']
194180
if 'CXX' in os.environ:
195181
cxx = os.environ['CXX']
196182
if 'LDSHARED' in os.environ:
@@ -543,43 +529,11 @@ def get_config_vars(*args):
543529
srcdir = os.path.join(base, _config_vars['srcdir'])
544530
_config_vars['srcdir'] = os.path.normpath(srcdir)
545531

532+
# OS X platforms require special customization to handle
533+
# multi-architecture, multi-os-version installers
546534
if sys.platform == 'darwin':
547-
kernel_version = os.uname()[2] # Kernel version (8.4.3)
548-
major_version = int(kernel_version.split('.')[0])
549-
550-
if major_version < 8:
551-
# On Mac OS X before 10.4, check if -arch and -isysroot
552-
# are in CFLAGS or LDFLAGS and remove them if they are.
553-
# This is needed when building extensions on a 10.3 system
554-
# using a universal build of python.
555-
for key in ('LDFLAGS', 'BASECFLAGS',
556-
# a number of derived variables. These need to be
557-
# patched up as well.
558-
'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
559-
flags = _config_vars[key]
560-
flags = re.sub('-arch\s+\w+\s', ' ', flags, re.ASCII)
561-
flags = re.sub('-isysroot [^ \t]*', ' ', flags)
562-
_config_vars[key] = flags
563-
564-
else:
565-
566-
# Allow the user to override the architecture flags using
567-
# an environment variable.
568-
# NOTE: This name was introduced by Apple in OSX 10.5 and
569-
# is used by several scripting languages distributed with
570-
# that OS release.
571-
572-
if 'ARCHFLAGS' in os.environ:
573-
arch = os.environ['ARCHFLAGS']
574-
for key in ('LDFLAGS', 'BASECFLAGS',
575-
# a number of derived variables. These need to be
576-
# patched up as well.
577-
'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
578-
579-
flags = _config_vars[key]
580-
flags = re.sub('-arch\s+\w+\s', ' ', flags)
581-
flags = flags + ' ' + arch
582-
_config_vars[key] = flags
535+
import _osx_support
536+
_osx_support.customize_config_vars(_config_vars)
583537

584538
if args:
585539
vals = []

Lib/distutils/tests/test_sysconfig.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,27 @@ def test_sysconfig_module(self):
102102
import sysconfig as global_sysconfig
103103
self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), sysconfig.get_config_var('CFLAGS'))
104104
self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), sysconfig.get_config_var('LDFLAGS'))
105-
self.assertEqual(global_sysconfig.get_config_var('LDSHARED'),sysconfig.get_config_var('LDSHARED'))
105+
106+
@unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'),'compiler flags customized')
107+
def test_sysconfig_compiler_vars(self):
108+
# On OS X, binary installers support extension module building on
109+
# various levels of the operating system with differing Xcode
110+
# configurations. This requires customization of some of the
111+
# compiler configuration directives to suit the environment on
112+
# the installed machine. Some of these customizations may require
113+
# running external programs and, so, are deferred until needed by
114+
# the first extension module build. With Python 3.3, only
115+
# the Distutils version of sysconfig is used for extension module
116+
# builds, which happens earlier in the Distutils tests. This may
117+
# cause the following tests to fail since no tests have caused
118+
# the global version of sysconfig to call the customization yet.
119+
# The solution for now is to simply skip this test in this case.
120+
# The longer-term solution is to only have one version of sysconfig.
121+
122+
import sysconfig as global_sysconfig
123+
if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'):
124+
return
125+
self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), sysconfig.get_config_var('LDSHARED'))
106126
self.assertEqual(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC'))
107127

108128

Lib/distutils/tests/test_util.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from distutils.sysconfig import get_config_vars
1414
from distutils import sysconfig
1515
from distutils.tests import support
16+
import _osx_support
1617

1718
class UtilTestCase(support.EnvironGuard, unittest.TestCase):
1819

@@ -92,6 +93,7 @@ def test_get_platform(self):
9293
('Darwin Kernel Version 8.11.1: '
9394
'Wed Oct 10 18:23:28 PDT 2007; '
9495
'root:xnu-792.25.20~1/RELEASE_I386'), 'i386'))
96+
_osx_support._remove_original_values(get_config_vars())
9597
get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3'
9698

9799
get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g '
@@ -105,6 +107,7 @@ def test_get_platform(self):
105107
sys.maxsize = cursize
106108

107109
# macbook with fat binaries (fat, universal or fat64)
110+
_osx_support._remove_original_values(get_config_vars())
108111
get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4'
109112
get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot '
110113
'/Developer/SDKs/MacOSX10.4u.sdk '
@@ -113,29 +116,34 @@ def test_get_platform(self):
113116

114117
self.assertEqual(get_platform(), 'macosx-10.4-fat')
115118

119+
_osx_support._remove_original_values(get_config_vars())
116120
os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.1'
117121
self.assertEqual(get_platform(), 'macosx-10.4-fat')
118122

119123

124+
_osx_support._remove_original_values(get_config_vars())
120125
get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot '
121126
'/Developer/SDKs/MacOSX10.4u.sdk '
122127
'-fno-strict-aliasing -fno-common '
123128
'-dynamic -DNDEBUG -g -O3')
124129

125130
self.assertEqual(get_platform(), 'macosx-10.4-intel')
126131

132+
_osx_support._remove_original_values(get_config_vars())
127133
get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot '
128134
'/Developer/SDKs/MacOSX10.4u.sdk '
129135
'-fno-strict-aliasing -fno-common '
130136
'-dynamic -DNDEBUG -g -O3')
131137
self.assertEqual(get_platform(), 'macosx-10.4-fat3')
132138

139+
_osx_support._remove_original_values(get_config_vars())
133140
get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot '
134141
'/Developer/SDKs/MacOSX10.4u.sdk '
135142
'-fno-strict-aliasing -fno-common '
136143
'-dynamic -DNDEBUG -g -O3')
137144
self.assertEqual(get_platform(), 'macosx-10.4-universal')
138145

146+
_osx_support._remove_original_values(get_config_vars())
139147
get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot '
140148
'/Developer/SDKs/MacOSX10.4u.sdk '
141149
'-fno-strict-aliasing -fno-common '
@@ -144,6 +152,7 @@ def test_get_platform(self):
144152
self.assertEqual(get_platform(), 'macosx-10.4-fat64')
145153

146154
for arch in ('ppc', 'i386', 'x86_64', 'ppc64'):
155+
_osx_support._remove_original_values(get_config_vars())
147156
get_config_vars()['CFLAGS'] = ('-arch %s -isysroot '
148157
'/Developer/SDKs/MacOSX10.4u.sdk '
149158
'-fno-strict-aliasing -fno-common '

Lib/distutils/unixccompiler.py

Lines changed: 6 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
DistutilsExecError, CompileError, LibError, LinkError
2424
from distutils import log
2525

26+
if sys.platform == 'darwin':
27+
import _osx_support
28+
2629
# XXX Things not currently handled:
2730
# * optimization/debug/warning flags; we just use whatever's in Python's
2831
# Makefile and live with it. Is this adequate? If not, we might
@@ -38,68 +41,6 @@
3841
# should just happily stuff them into the preprocessor/compiler/linker
3942
# options and carry on.
4043

41-
def _darwin_compiler_fixup(compiler_so, cc_args):
42-
"""
43-
This function will strip '-isysroot PATH' and '-arch ARCH' from the
44-
compile flags if the user has specified one them in extra_compile_flags.
45-
46-
This is needed because '-arch ARCH' adds another architecture to the
47-
build, without a way to remove an architecture. Furthermore GCC will
48-
barf if multiple '-isysroot' arguments are present.
49-
"""
50-
stripArch = stripSysroot = False
51-
52-
compiler_so = list(compiler_so)
53-
kernel_version = os.uname()[2] # 8.4.3
54-
major_version = int(kernel_version.split('.')[0])
55-
56-
if major_version < 8:
57-
# OSX before 10.4.0, these don't support -arch and -isysroot at
58-
# all.
59-
stripArch = stripSysroot = True
60-
else:
61-
stripArch = '-arch' in cc_args
62-
stripSysroot = '-isysroot' in cc_args
63-
64-
if stripArch or 'ARCHFLAGS' in os.environ:
65-
while True:
66-
try:
67-
index = compiler_so.index('-arch')
68-
# Strip this argument and the next one:
69-
del compiler_so[index:index+2]
70-
except ValueError:
71-
break
72-
73-
if 'ARCHFLAGS' in os.environ and not stripArch:
74-
# User specified different -arch flags in the environ,
75-
# see also distutils.sysconfig
76-
compiler_so = compiler_so + os.environ['ARCHFLAGS'].split()
77-
78-
if stripSysroot:
79-
try:
80-
index = compiler_so.index('-isysroot')
81-
# Strip this argument and the next one:
82-
del compiler_so[index:index+2]
83-
except ValueError:
84-
pass
85-
86-
# Check if the SDK that is used during compilation actually exists,
87-
# the universal build requires the usage of a universal SDK and not all
88-
# users have that installed by default.
89-
sysroot = None
90-
if '-isysroot' in cc_args:
91-
idx = cc_args.index('-isysroot')
92-
sysroot = cc_args[idx+1]
93-
elif '-isysroot' in compiler_so:
94-
idx = compiler_so.index('-isysroot')
95-
sysroot = compiler_so[idx+1]
96-
97-
if sysroot and not os.path.isdir(sysroot):
98-
log.warn("Compiling with an SDK that doesn't seem to exist: %s",
99-
sysroot)
100-
log.warn("Please check your Xcode installation")
101-
102-
return compiler_so
10344

10445
class UnixCCompiler(CCompiler):
10546

@@ -168,7 +109,8 @@ def preprocess(self, source, output_file=None, macros=None,
168109
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
169110
compiler_so = self.compiler_so
170111
if sys.platform == 'darwin':
171-
compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs)
112+
compiler_so = _osx_support.compiler_fixup(compiler_so,
113+
cc_args + extra_postargs)
172114
try:
173115
self.spawn(compiler_so + cc_args + [src, '-o', obj] +
174116
extra_postargs)
@@ -247,7 +189,7 @@ def link(self, target_desc, objects,
247189
linker[i] = self.compiler_cxx[i]
248190

249191
if sys.platform == 'darwin':
250-
linker = _darwin_compiler_fixup(linker, ld_args)
192+
linker = _osx_support.compiler_fixup(linker, ld_args)
251193

252194
self.spawn(linker + ld_args)
253195
except DistutilsExecError as msg:

0 commit comments

Comments
 (0)