Commits

Ronald Oussoren committed aec5ce5

Explictly try to find a working compiler, needed because some
binary installers for Python have a reference to a compiler that
doesn't work on OSX 10.8 (or rather 'with Xcode 4').

See also <http://bugs.python.org/issue13590>;

Fixes #30

  • Participants
  • Parent commits f8ff545

Comments (0)

Files changed (41)

pyobjc-core/NEWS.txt

   This is mostly done for the PyObjC unittests and shouldn't affect user
   code.
 
+- Issue #30: Explicitly check if the compiler works, and try to
+  fall back to clang if it doesn't. This uses a simular algoritm as
+  the fix for <http://bugs.python.org/issue13590> in Python's tracker.
+
 - Issue #22: Reimplement support for bridgesupport files
 
   This reintroduces ``objc.parseBridgeSupport`` and 
   code that creates the block is compiled using a recent enough compiler (although "recent
   enough" is fairly old by now)
 
+
 Version 2.4.1
 -------------
 

pyobjc-core/Tools/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-core/setup.py

 
         return result
 
+
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
+
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+        cflags += CFLAGS
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
+
+
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
         build_ext.build_ext.run(self)
         extensions = self.extensions
         self.extensions = [
 if '-O0' in get_config_var('CFLAGS'):
     print ("Change -O0 to -O1")
     vars = get_config_vars()
-    vars['CFLAGS'] = vars['CFLAGS'].replace('-O0', '-O1')
+    for k in vars:
+        if isinstance(vars[k], str) and '-O0' in vars[k]:
+            vars[k] = vars[k].replace('-O0', '-O1')
 
 OBJC_LDFLAGS = frameworks('CoreFoundation', 'Foundation', 'Carbon')
 
 # a binary that runs on other releases of the OS without using a particular SDK.
 CFLAGS.extend(['-isysroot', '/'])
 OBJC_LDFLAGS.extend(['-isysroot', '/'])
-
-
-
 CFLAGS.append('-Ibuild/codegen/')
 
 # Patch distutils: it needs to compile .S files as well.

pyobjc-framework-Accounts/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-AddressBook/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-AppleScriptKit/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-AppleScriptObjC/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-Automator/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-CFNetwork/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-CalendarStore/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-Cocoa/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-Collaboration/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-CoreData/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-CoreLocation/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-CoreText/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-DictionaryServices/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-EventKit/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-ExceptionHandling/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-FSEvents/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+
+        binfile = os.path.basename(binfile)
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+    return True
+
+def _fixup_compiler():
+    if 'CC' in os.environ:
+        # CC is in the environment, always use explicit 
+        # overrides.
+        return
+
+    cc = oldcc = get_config_var('CC').split()[0]
+    cc = _find_executable(cc)
+    if cc is not None and os.path.basename(cc).startswith('gcc'):
+        # Check if compiler is LLVM-GCC, that's known to
+        # generate bad code.
+        data = os.popen("'%s' --version 2>/dev/null"%(
+            cc.replace("'", "'\"'\"'"),)).read()
+        if 'llvm-gcc' in data:
+            cc = None
+
+    if not _working_compiler(cc):
+        cc = None
+
+    if cc is None:
+        # Default compiler is not useable, try finding 'clang'
+        cc = _find_executable('clang')
+        if cc is None:
+            cc = os.popen("/usr/bin/xcrun -find clang").read()
+
+    if not cc:
+        raise SystemExit("Cannot locate compiler candidate")
+
+    if not _working_compiler(cc):
+        raise SystemExit("Cannot locate a working compiler")
+
+    if cc != oldcc:
+        print("Use '%s' instead of '%s' as the compiler"%(
+            cc, oldcc))
+
+        vars = get_config_vars()
+        for env in ('BLDSHARED', 'LDSHARED', 'CC', 'CXX'):
+            if env in vars and env not in os.environ:
+                split = vars[env].split()
+                split[0] = cc if env != 'CXX' else cc + '++'
+                vars[env] = ' '.join(split)
 
 class pyobjc_build_ext (build_ext.build_ext):
     def run(self):
+        _fixup_compiler()
 
         # Ensure that the PyObjC header files are available
         # in 2.3 and later the headers are in the egg,

pyobjc-framework-InputMethodKit/pyobjc_setup.py

 
 from setuptools.command import test
 from setuptools.command import build_py
+from distutils.sysconfig import get_config_var, get_config_vars
+
 
 from distutils import log
 
 
         return result
 
+def _find_executable(executable):
+    if os.path.isfile(executable):
+        return executable
 
+    else:
+        for p in os.environ['PATH'].split(os.pathsep):
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                return f
+    return None
+
+def _working_compiler(executable):
+    import tempfile, subprocess, shlex
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.c') as fp:
+        fp.write('#include <stdarg.h>\nint main(void) { return 0; }\n')
+        fp.flush()
+
+        cflags = get_config_var('CFLAGS')
+        cflags = shlex.split(cflags)
+
+        p = subprocess.Popen([
+            executable, '-c', fp.name] + cflags, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        exit = p.wait()
+        if exit != 0:
+            return False
+        
+        binfile = fp.name[:-1] + 'o'
+        if os.path.exists(binfile):
+            os.unlink(binfile)
+
+