Introduction
In the last few years, Apple has been moving from the GCC toolchain to the LLVM toolchain. Indeed, starting from OS X 10.9 (Mavericks) libc++
has become the default C++ runtime library, thus definitely dropping the support for the otherwise widely-used libstdc++
.
It is important to notice that, even though libc++
has been specifically designed to be compatible with libstdc++
, the two runtime library are in fact partially binary incompatibile. For instance, it is not possible to link any library compiled against libstdc++
that uses std::string
with a program that uses libc++
. Even though the API is apparently the same, the two runtime libraries differ in the implementation of std::string
. Hence, to avoid the risk of having code compiled against different runtime libraries in the same address space, in libc++
, the code for std::string
is actually placed in the non-standard namespace std::_1::string
.
The solution to this messy situation is to adopt one C++ runtime library and be coherent with all the libraries and code you have to deal with.
The problem
Unfortunately, the afore-mentioned thumb rule becomes really difficult to apply for libraries that you cannot re-compile.
Recently, I've been working on a project that heavily relies on the new-ish C++ features from C++11. As Apple is moving away from libstdc++
, the system-provided libstdc++
has not been updated since OS X 10.7 (Lion) and it lacks any C++11 facility. Unfortunately, I also need to use the ILOG CPLEX solver, which is compiled against libstdc++
. As a result, I found myself in a tough situation: I needed to use libstdc++
to use CPLEX, but I also needed to use clang
in conjuction with libc++
to take advantage of all the beautiful features of C++11 on OS X.
Ideally, the compilation should take place as follows:
clang++ -std=c++11 -stdlib=libstdc++ source.cpp -o source
The solution
The solution strategy proceeds as follows:
- We are going to compile the latest (stable)
gcc
suite from source - We are going to compile the latest
clang
compiler from SVN trunk. In doing so, we will madeclang
point to thelibstdc++
we obtained in the previous step
By doing so, it will be possibile to use the clang
compiler in conjunction with the latest libstdc++
.
In order not to interfere with the system-provided libraries and binaries, we are going to place everything into the /opt
directory.
The installation procedure is adapted from here
Compiling GCC (Pt I): dependencies
Before compiling gcc itself, we have to satisfy few dependencies, namely:
- The
gmp
library - The
mpfr
andmpc
library - The
ppl
andcloog-ppl
library
The gmp
library
Download the latest library from gmplib.org, at the time of writing the latest version is gmp-5.1.3
.
In case no lzip
decompressor is available on your system, you can install lzip
from MacPorts with:
sudo port install lzip
and unzip the library with:
lzip -d gmp-5.1.3.lz
tar xfv gmp-5.1.3.tar
We are now ready to actually build gmp
:
mkdir build
cd build
../configure --prefix=/opt/gcc --enable-cxx
make -j`sysctl -n hw.logicalcpu`
make check
sudo make install
The mpfr
library
Download the latest library from mpfr.org, at the time of writing the latest version is mpfr-3.1.2
.
The building process is similar to what we have done previously:
cd path/to/mpfr
mkdir build
cd build
./configure --prefix=/opt/gcc --with-gmp=/opt/gcc
make -j`sysctl -n hw.logicalcpu`
sudo make install
The mpc
library
Download the latest library from multiprecision.org,at the time of writing the latest version in mpc-1.0.1
.
The building process is similar to what we have done previously:
cd path/to/ppl
mkdir build
cd build
../configure --prefix=/opt/gcc --with-gmp=/opt/gcc --with-mpfr=/opt/gcc
make -j`sysctl -n hw.logicalcpu`
sudo make install
The ppl
and cloog-ppl
library
Download the latest ppl
library from bugseng.com, at the time of writing the latest version is ppl-1.1
.
The building process is similar to what we have done previously:
cd path/to/ppl
../configure --prefix=/opt/gcc --with-gmp=/opt/gcc
Download the latest cloog-ppl
library from gnu.org, at the time of writing the latest version is cloog-ppl-0.15.11
.
After decompressing the archive, we need to slightly modify the configure
script. This is because the library is expecting a version of the ppl
library following the pattern 0.1x
, while we are currently using ppl-1.1
. Hence, open the configure
script with your favorite editor and alter line 11225
, from:
#if PPL_VERSION_MAJOR != 0 || PPL_VERSION_MINOR < 10
to:
#if PPL_VERSION_MAJOR != 1|| PPL_VERSION_MINOR != 1
We are now ready to compile:
cd path/to/cloog-ppl
mkdir build
cd build
../configure --prefix=/opt/gcc --with-gmp=/opt/gcc --with-ppl=/opt/gcc
make -j`sysctl -n hw.logicalcpu`
sudo make install
The last two steps will surely take a while to complete. When everything is done you should be able to execute the following command:
/opt/gcc/bin/g++ --verbose
Testing the compiler
Before diving into the compilation procedure for clang
let's perform a preliminary test to check that everything went fine with GCC. In order to do so, we will try to compile the following trivial C++11 code:
#include <iostream>
#include <random>
int main() {
int&& x = 10;
std::cout << x << std::endl;
return 0;
}
by using the following command:
/opt/gcc/bin/g++-4.8 -std=c++11 random.cpp -o random
Additionally, we can check that the right libstdc++
library has been linked with the binary:
otool -L random
which returns:
random:
/opt/gcc/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.19.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
/opt/gcc/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
Please notice how we are not using the system-provided (i.e., host) libstdc++
, which is located in /usr/lib/libstdc++.6.dylib
.
Compiling clang
At this point we are ready to build clang
against the brand-new libstdc++
library we obtained at the previous steps.
Apart from few extra arguments, the procedure is the same as the one described in the official getting started page.
mkdir clang
cd clang
svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
cd llvm/tools
svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
cd ../..
cd llvm/tools/clang/tools
svn co http://llvm.org/svn/llvm-project/clang-tools-extra/trunk extra
cd ../../../..
cd llvm/projects
svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt
cd ../..
Before compiling the whole stuff, we need to inform clang
on where to find the gcc
headers and the libstdc++
library itself.
In order to do so, we first modify the file llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp
at line 364
, from:
case llvm::Triple::x86:
case llvm::Triple::x86_64:
AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1",
"i686-apple-darwin10", "", "x86_64", triple);
AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.0.0",
"i686-apple-darwin8", "", "", triple);
break;
to:
case llvm::Triple::x86:
case llvm::Triple::x86_64:
AddGnuCPlusPlusIncludePaths("/opt/gcc/include/c++/4.8.3",
"x86_64-apple-darwin12.5.0", "", "", triple);
break;
Notice that the first and second argument of the AddGnuCPlusPlusIncludePaths
member function depend on the gcc
version you decided to compile and on the OS X release that you are using. To find out what to use, it is sufficient to take a look at /opt/gcc/include/c++
and tailor the function call to your own configuration.
Second, we re-write the Toolchain::CST_libstdcxx
member function in llvm/tools/clang/lib/Driver/ToolChains.cpp
(line 594
), going from:
case ToolChain::CST_Libstdcxx: {
// Unfortunately, -lstdc++ doesn't always exist in the standard search path;
... LOT OF STUFF HERE
// Otherwise, let the linker search.
CmdArgs.push_back("-lstdc++");
break;
}
to:
case ToolChain::CST_Libstdcxx: {
CmdArgs.push_back("/opt/gcc/lib/libstdc++.6.dylib");
break;
}
As reported, we are basically hardcoding the path where to find the libstdc++
library we compiled at the previous steps.
We are now ready to build clang
:
mkdir build
cd build
../llvm/configure --prefix=/opt/gcc --with-gcc-toolchain=/opt/gcc --enable-optimized --enable-targets=host-only
make -j`sysctl -n hw.logicalcpu`
sudo make install
As before, the last two steps will take quite a while to complete (but not as much as with gcc
)
Testing the compiler
We can now try to compile the same source file as before, by using clang
and libstdc++
:
/opt/gcc/bin/clang++ -std=c++11 -stdlib=libstdc++ random.cpp -o random
Again, we can inspect the binary to be sure that everything went as expected:
otool -L random
which should report:
random:
/opt/gcc/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.19.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
So it seems that we succeded in obtaining the ideal command we mentioned at the beginning. Still, we cannot execute the following command:
clang++ -std=c++11 -stdlib=libc++ random.cpp -o random
That's because clang
is not shipped together with the libc++
runtime library. Namely, our copy of clang
is expecting to find libc++
headers in /opt/gcc/include/c++/v1
.
A possible solution is to bind our copy of clang
with the system-provided (i.e., host) libc++
. In order to do so it is sufficient to create a symbolic link:
cd /opt/gcc/include/c++/
sudo ln -s /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/c++/v1 .
Note that, starting from Xcode 6.0.1, the includes have been moved in a different location, hence the command should be:
cd /opt/gcc/include/c++/
sudo ln -s /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1 .
Use clang
with Xcode
In order to use our local copy of clang
with Xcode, we need at first to create a symbolic link:
sudo ln -s /opt /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/opt
this is due to the fact that Xcode will use the compiler's isysroot
flag to alter the root folder from /
to
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.X.sdk/
where X
denotes the OS X release. Consequently, we need to be able to access from there the /opt
folder.
Open a new Finder window/tab and go to:
~/Library/Application Support/Developer/Shared/Xcode/Plug-ins
Feel free to create the folder in case it doesn't exist. Once created, place the compiler plugin in the folder. Note that the plugin is compliant with Xcode up to version 7.3.
Miscellaneous
While trying to figure out what to do, a quick hack was to pass the following flags to a local copy of clang
:
clang++ -I/opt/gcc/include/c++/4.8.3/ -I/opt/gcc/include/c++/4.8.3/x86_64-apple-darwin13.0.0/ -L/opt/gcc/lib
Even if this has been made outdated by the presented procedure, it is still good to remember for future reference.
References
- How to compile GCC 4.7.1 on Mac OS X Lion
- Getting started with clang
- What is a good way of handling ABI-differences between libc++ and the older libstdc++?
- Clang-dev mailing list
- When enabling C++11 with stdlibc++ 4.7, clang error out, while gcc compiles fine
- Build Clang With Stdlibc++ 4.7 in Mac OS
- Can't link Hello World-program program on Linux
- Where are libc++ headers located on Mac OS X Mavericks?