So, C++11 is finally out! However, we still have to wait for the compilers to catch up. Some compilers are faster than others, however, and clang is certainly one of the fastest of the lot. I've recently set-up a simple trunk environment for clang and found it to be pretty stable - at least for learning purposes - so I decided to share my notes.
I started my wonderings with this post: Compiling llvm, clang and libc++ on Linux.However, I must say I found their approach to be a bit too... hacky... so I adjusted it somewhat. You won't fail to notice the similarities though. For the remainder of the article, I assume you have git, svn, cmake, and all other usual development packages installed; if not, apt-get them before proceeding. I also decided to compile clang using clang stable, for giggles mainly, so I got my hands on the latest clang (2.9) via the usual apt-get means. Without further ado, lets get on with it.
The first thing to do is to checkout the source code. I use git as an svn client, but feel free to use svn directly. Unfortunately, I couldn't get git svn to use modules, so I ended up having to do an svn checkout of clang. Oh well.
Checkout steps:
$ mkdir llvm$ cd llvm$ git svn clone -r HEAD http://llvm.org/svn/llvm-project/llvm/trunk llvm_src$ cd llvm_src/tools$ svn checkout http://llvm.org/svn/llvm-project/cfe/trunk clang$ cd ../../$ git svn clone -r HEAD http://llvm.org/svn/llvm-project/libcxx/trunk libcxx_src
We now have llvm, clang and the standard library. However, before we can do a build we must do the include headers hack:
$ cd llvm_src/tools/clang/lib/Frontend$ emacs InitHeaderSearch.cpp
"Apply" the following patch, replacing marco with your username:
// FIXME: temporary hack: hard-coded paths.- AddPath("/usr/local/include", System, true, false, false);+ AddPath("/home/marco/local/include/c++/v1", System, true, false, false);
Exit emacs. The source is now ready to build. As we are civilised folk, we do out of source builds:
$ cd - # e.g. back at the llvm top level directory$ mkdir llvm_build$ cd llvm_build$ CC=clang CXX=clang++ cmake -DCMAKE_INSTALL_PREFIX=/home/marco/local ../llvm_src -j4
A couple of notes:
- If you want to do a default gcc build you can omit the CC and CXX variables. If you have multiple gcc versions, just set them accordingly (e.g. CC=gcc-4.5 CXX=g++-4.5, etc.).
- Ideally you want the install directory to be a throw away directory that you can wipe out whenever, so don't put anything else on it.
- Feel free to bump the -j4 to a sensible number depending on the cores you have available. If you are not using the machine, a -j6 or even -j8 on a quad-core tends to speed things up a lot.
You can now install clang:
$ make install
This will copy all the libs and binaries over to the install directory (~/local on our case). Now we can build and install the standard library:
$ cd ../libcxx_build$ CC=clang CXX=clang++ cmake -DCMAKE_INSTALL_PREFIX=/home/marco/local ../libcxx_src -j4$ make install
At this point you now have a complete, if somewhat bare, clang trunk environment to play with. So lets create a simple program to test it:
$ cd ..$ mkdir simple_src$ mkdir simple_build$ cd simple_src$ emacs simple.cpp
Type a simple hello world:
#include "iostream" // can't get the angle-brackets to work..#include "fstream" // can't get the angle-brackets to work..using namespace std;int main(int, char**) {std::cout << "Hello c++11 world!" << std::endl;return 0;}
On the same directory, create a trivial CMakeLists.txt file (I'm making it a bit more involved as I have a template...):
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)project(simple)## compiler and linker flags## enable as many warnings as possibleset(warning_flags "-Wall -Wextra")# issue all the warnings demanded by strict iso c and iso c++set(warning_flags "${warning_flags} -pedantic")# treat warnings as errorsset(warning_flags "${warning_flags} -Werror")# definition shadows anotherset(warning_flags "${warning_flags} -Wshadow")# do not issue warnings for system headersset(warning_flags "${warning_flags} -Wno-system-headers")# overloaded virtual function has a different signatureset(warning_flags "${warning_flags} -Woverloaded-virtual")# make string constants const char*set(warning_flags "${warning_flags} -Wwrite-strings")# enable RTTIset(other_flags "-frtti")# set the flagsset(CMAKE_CC_COMPILER /home/marco/local/bin/clang)set(CMAKE_CXX_COMPILER /home/marco/local/bin/clang++)set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/home/marco/local/lib")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${optimisation_flags}")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${warning_flags}")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${profiling_flags}")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${other_flags}")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc++")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")set(app "simple")set(files "")file(GLOB_RECURSE files RELATIVE"${CMAKE_CURRENT_SOURCE_DIR}/""${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")add_executable(${app} ${files})
We can now compile our test program! Exit emacs and type:
$ cd ..$ cd simple_build$ cmake ../simple_src$ make
To run the binary we will need to ensure the C++ library is on the program loader's path:
$ LD_LIBRARY_PATH=/home/marco/local/lib ./simpleHello c++11 world!
One interesting thing to notice is that we still have two C++ libraries:
$ LD_LIBRARY_PATH=/home/marco/local/lib ldd simple | grep c++libc++.so.1 => /home/marco/local/lib/libc++.so.1 (0x00007fa5b5188000)libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fa5b463f000)
Something for a further article though.
With this set-up you can now follow clang trunk development; just git svn rebase, and svn update all the checkout directories, rebuild and re-install. Every so often you may need to delete the whole of ~/local and start again, but other than that you should be good to go.