Skip to content
Snippets Groups Projects
Commit b369be65 authored by Ben Boeckel's avatar Ben Boeckel Committed by Todd Gamblin
Browse files

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
parent 1925db5c
Branches
Tags
No related merge requests found
...@@ -223,6 +223,8 @@ def set_compiler_environment_variables(pkg, env): ...@@ -223,6 +223,8 @@ def set_compiler_environment_variables(pkg, env):
for mod in compiler.modules: for mod in compiler.modules:
load_module(mod) load_module(mod)
compiler.setup_custom_environment(env)
return env return env
......
...@@ -114,9 +114,15 @@ def fc_rpath_arg(self): ...@@ -114,9 +114,15 @@ def fc_rpath_arg(self):
def __init__(self, cspec, operating_system, def __init__(self, cspec, operating_system,
paths, modules=[], alias=None, **kwargs): paths, modules=[], alias=None, **kwargs):
self.operating_system = operating_system
self.spec = cspec
self.modules = modules
self.alias = alias
def check(exe): def check(exe):
if exe is None: if exe is None:
return None return None
exe = self._find_full_path(exe)
_verify_executables(exe) _verify_executables(exe)
return exe return exe
...@@ -138,11 +144,6 @@ def check(exe): ...@@ -138,11 +144,6 @@ def check(exe):
if value is not None: if value is not None:
self.flags[flag] = value.split() self.flags[flag] = value.split()
self.operating_system = operating_system
self.spec = cspec
self.modules = modules
self.alias = alias
@property @property
def version(self): def version(self):
return self.spec.version return self.spec.version
...@@ -269,6 +270,21 @@ def check(key): ...@@ -269,6 +270,21 @@ def check(key):
successful.reverse() successful.reverse()
return dict(((v, p, s), path) for v, p, s, path in successful) 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): def __repr__(self):
"""Return a string representation of the compiler toolchain.""" """Return a string representation of the compiler toolchain."""
return self.__str__() return self.__str__()
......
...@@ -23,11 +23,14 @@ ...@@ -23,11 +23,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
############################################################################## ##############################################################################
import re import re
import os
import spack
import spack.compiler as cpr import spack.compiler as cpr
from spack.compiler import * from spack.compiler import *
from spack.util.executable import * from spack.util.executable import *
import llnl.util.tty as tty import llnl.util.tty as tty
from spack.version import ver from spack.version import ver
from shutil import copytree, ignore_patterns
class Clang(Compiler): class Clang(Compiler):
...@@ -107,3 +110,79 @@ def default_version(cls, comp): ...@@ -107,3 +110,79 @@ def default_version(cls, comp):
cpr._version_cache[comp] = ver cpr._version_cache[comp] = ver
return cpr._version_cache[comp] 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)
...@@ -42,7 +42,8 @@ def url_for_version(self, version): ...@@ -42,7 +42,8 @@ def url_for_version(self, version):
def install(self, spec, prefix): def install(self, spec, prefix):
with working_dir('source'): with working_dir('source'):
configure('--prefix={0}'.format(prefix)) configure('--prefix={0}'.format(prefix),
'--enable-rpath')
make() make()
make('check') make('check')
......
...@@ -183,7 +183,6 @@ def common_config_args(self): ...@@ -183,7 +183,6 @@ def common_config_args(self):
'-no-xcb-xlib', '-no-xcb-xlib',
'-no-pulseaudio', '-no-pulseaudio',
'-no-alsa', '-no-alsa',
'-no-gtkstyle',
]) ])
if '@4' in self.spec and sys.platform == 'darwin': if '@4' in self.spec and sys.platform == 'darwin':
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment