Mock up Xcode devdir to make Qt5 work on Mac OS X (#1832)

* build_environment: allow compilers to set up an environment

* clang: mock up a toolchain directory for xcode

Some projects ignore CC and CXX flags and instead use xcode to find the
toolchain. Clang on Apple should set up the environment properly.
Arguably, every compiler could do this on Apple, but let's see how this
works out just for AppleClang for now.

The Documentation directory is ~1.7G and the excluded platforms add up
to about 7G. Ignoring swift saves another 500M. The resulting Xcode.app
copy is in the 2G range.

* compiler: set member variables early

This is required so that later methods can query things such as the
version of the compiler.

* compiler: support finding the real path of the compiler

On Apple, the /usr/bin compilers are actually wrapping tools themselves
which query xcrun for the currently selected Xcode installation. Pierce
this veil and get the real, full path the to underlying compilers
instead.

* icu4c: install with rpath

On macOS, icu installs with a library ID of the library name. Enabling
rpath makes its ID its full installed path which lets Qt5 link against
it successfully.

* qt: no -no-gtkstyle flag on Qt5 on macOS
This commit is contained in:
Ben Boeckel 2016-10-12 12:58:12 -04:00 committed by Todd Gamblin
parent 1925db5c77
commit b369be65d7
5 changed files with 104 additions and 7 deletions

View File

@ -223,6 +223,8 @@ def set_compiler_environment_variables(pkg, env):
for mod in compiler.modules:
load_module(mod)
compiler.setup_custom_environment(env)
return env

View File

@ -114,9 +114,15 @@ def fc_rpath_arg(self):
def __init__(self, cspec, operating_system,
paths, modules=[], alias=None, **kwargs):
self.operating_system = operating_system
self.spec = cspec
self.modules = modules
self.alias = alias
def check(exe):
if exe is None:
return None
exe = self._find_full_path(exe)
_verify_executables(exe)
return exe
@ -138,11 +144,6 @@ def check(exe):
if value is not None:
self.flags[flag] = value.split()
self.operating_system = operating_system
self.spec = cspec
self.modules = modules
self.alias = alias
@property
def version(self):
return self.spec.version
@ -269,6 +270,21 @@ def check(key):
successful.reverse()
return dict(((v, p, s), path) for v, p, s, path in successful)
def _find_full_path(self, path):
"""Return the actual path for a tool.
Some toolchains use forwarding executables (particularly Xcode-based
toolchains) which can be manipulated by external environment variables.
This method should be used to extract the actual path used for a tool
by finding out the end executable the forwarding executables end up
running.
"""
return path
def setup_custom_environment(self, env):
"""Set any environment variables necessary to use the compiler."""
pass
def __repr__(self):
"""Return a string representation of the compiler toolchain."""
return self.__str__()

View File

@ -23,11 +23,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import re
import os
import spack
import spack.compiler as cpr
from spack.compiler import *
from spack.util.executable import *
import llnl.util.tty as tty
from spack.version import ver
from shutil import copytree, ignore_patterns
class Clang(Compiler):
@ -107,3 +110,79 @@ def default_version(cls, comp):
cpr._version_cache[comp] = ver
return cpr._version_cache[comp]
def _find_full_path(self, path):
basename = os.path.basename(path)
if not self.is_apple or basename not in ('clang', 'clang++'):
return super(Clang, self)._find_full_path(path)
xcrun = Executable('xcrun')
full_path = xcrun('-f', basename, output=str)
return full_path.strip()
def setup_custom_environment(self, env):
"""Set the DEVELOPER_DIR environment for the Xcode toolchain.
On macOS, not all buildsystems support querying CC and CXX for the
compilers to use and instead query the Xcode toolchain for what
compiler to run. This side-steps the spack wrappers. In order to inject
spack into this setup, we need to copy (a subset of) Xcode.app and
replace the compiler executables with symlinks to the spack wrapper.
Currently, the stage is used to store the Xcode.app copies. We then set
the 'DEVELOPER_DIR' environment variables to cause the xcrun and
related tools to use this Xcode.app.
"""
super(Clang, self).setup_custom_environment(env)
if not self.is_apple:
return
xcode_select = Executable('xcode-select')
real_root = xcode_select('--print-path', output=str).strip()
real_root = os.path.dirname(os.path.dirname(real_root))
developer_root = os.path.join(spack.stage_path,
'xcode-select',
self.name,
str(self.version))
xcode_link = os.path.join(developer_root, 'Xcode.app')
if not os.path.exists(developer_root):
tty.warn('Copying Xcode from %s to %s in order to add spack '
'wrappers to it. Please do not interrupt.'
% (real_root, developer_root))
# We need to make a new Xcode.app instance, but with symlinks to
# the spack wrappers for the compilers it ships. This is necessary
# because some projects insist on just asking xcrun and related
# tools where the compiler runs. These tools are very hard to trick
# as they do realpath and end up ignoring the symlinks in a
# "softer" tree of nothing but symlinks in the right places.
copytree(real_root, developer_root, symlinks=True,
ignore=ignore_patterns('AppleTV*.platform',
'Watch*.platform',
'iPhone*.platform',
'Documentation',
'swift*'))
real_dirs = [
'Toolchains/XcodeDefault.xctoolchain/usr/bin',
'usr/bin',
]
bins = ['c++', 'c89', 'c99', 'cc', 'clang', 'clang++', 'cpp']
for real_dir in real_dirs:
dev_dir = os.path.join(developer_root,
'Contents',
'Developer',
real_dir)
for fname in os.listdir(dev_dir):
if fname in bins:
os.unlink(os.path.join(dev_dir, fname))
os.symlink(os.path.join(spack.build_env_path, 'cc'),
os.path.join(dev_dir, fname))
os.symlink(developer_root, xcode_link)
env.set('DEVELOPER_DIR', xcode_link)

View File

@ -42,7 +42,8 @@ def url_for_version(self, version):
def install(self, spec, prefix):
with working_dir('source'):
configure('--prefix={0}'.format(prefix))
configure('--prefix={0}'.format(prefix),
'--enable-rpath')
make()
make('check')

View File

@ -183,7 +183,6 @@ def common_config_args(self):
'-no-xcb-xlib',
'-no-pulseaudio',
'-no-alsa',
'-no-gtkstyle',
])
if '@4' in self.spec and sys.platform == 'darwin':