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:

  1. We are going to compile the latest (stable) gcc suite from source
  2. We are going to compile the latest clang compiler from SVN trunk. In doing so, we will made clang point to the libstdc++ 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 and mpc library
  • The ppl and cloog-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