clang++ by default compiles for system “native” C++ library. On macOS, that is LLVM’s
libc++. But what happens if happen to have a lot of binaries (on in my case) libraries compiled with
g++ using its own
Why was I doing that in the first place? Work stuff is mostly Linux and therefore uses gcc, and the Apple-provided clang that ships with Xcode.app doesn’t support OpenMP, so I ended up building a lot of stuff with a homebrew-sourced gcc. But it turns out that gcc’s OpenMP support on macOS (in particular the implementation of the synchronization primitives in
libgomp) is surprisingly slow (on Linux it’s much better). I ended up with a piece of OpenMP code whose performance was so bad, I need to cross check what the cause was, and so I installed a (non-Apple) clang with which I wanted to compile the code in question, but preferably without having to rebuild all my
gcc-compiled (and therefore
The following seems to work for a homebrew-installed
gcc 9.3 and
llvm (clang) 10:
CXX=/usr/local/opt/llvm/bin/clang++ CXXFLAGS="-stdlib=libstdc++ -stdlib++-isystem /usr/local/Cellar/gcc/9.3.0_1/include/c++/9.3.0/ -cxx-isystem /usr/local/Cellar/gcc/9.3.0_1/include/c++/9.3.0/x86_64-apple-darwin19" LDFLAGS="-stdlib=libstdc++ -L /usr/local/Cellar/gcc/9.3.0_1/lib/gcc/9 -L /usr/local/opt/llvm/lib"
The last part of the link flags that add the library path back to the homebrew clang are only required so that that
clang‘s OpenMP support can find its
libomp support library.
This works unless you used something like
libstdc++‘s implementation of
std::call_once, because that needs internal symbols that are declared as
__thread, which with how homebrew’s
gcc is configurationed specifically or gcc on Darwin behaves generally used “emulated thread-local storage” (
tls), which in turn requires mangling that
__thread declared symbol differently 😔. clang even knows about that (see
-femulated-tls), but when I tried to use that variant (which did indeed link without complaint) I got weird
malloc errors in
emutls functions, and it was easier for me to replace
std::call_once with a hack than figuring out where those errors came from.