CMake part 2: Compiler flags

December 21, 2009 | 4 Minute Read

It’s not the first time I talk about CMake in this blog, for the introduction read here. Now it’s time to explore the CMake syntax further. The first CMakeLists.txt looked like this:

cmake_minimum_required(VERSION 2.6)
project(HELLO)

set(HELLO_SRCS main.cpp foo.cpp)
add_executable(hello ${HELLO_SRCS})

As already explained, it successfully creates an executable called “hello” using the main.cpp and foo.cpp source files. But let’s see exactly what CMake does to compile these files. To do so, it is possible to use the commands:

mkdir build && cd build
cmake ../
make VERBOSE=1

The interesting thing here is the VERBOSE=1 option. By default CMake hides the options passed to the compiler, and displays a higher level status indicator with the build completion percentage together with the name of the file currently being built (a much more elegant solution than autoconf). But if the goal is to see the compiler flags used, it is always possible to override this behaviour with the VERBOSE=1 option.

Here is the relevant part of the printout:

[...]
[ 50%] Building CXX object CMakeFiles/hello.dir/main.cpp.o
/usr/bin/c++     -o CMakeFiles/hello.dir/main.cpp.o -c /tmp/cmaketest/main.cpp
[100%] Building CXX object CMakeFiles/hello.dir/foo.cpp.o
/usr/bin/c++     -o CMakeFiles/hello.dir/foo.cpp.o -c /tmp/cmaketest/foo.cpp
Linking CXX executable hello
/usr/bin/c++      CMakeFiles/hello.dir/main.cpp.o CMakeFiles/hello.dir/foo.cpp.o  -o hello
-rdynamic
[...]

Now, if you’ve read my previous blog post on GCC’s compiler flags, you might probably not like what you see, since no optimization flag has been passed to GCC and as a result, your program won’t run as fast as it should.

That’s because no build type has been specified to CMake. The build type is a feature most IDE have, it allows you to compile your program in “debug” mode, for easily single-stepping through it with a debugger, or in “release” mode, with speed optimization enabled.

To fix this you simply need to specify a build type in the CMakeLists.txt file, in this way:

set(CMAKE_BUILD_TYPE Release)

at the end of your CMakeLists.txt file. Of course, change “Release” with “Debug” for debug builds.

With the Release build type, the options passed to the compiler are these:

[...]
[ 50%] Building CXX object CMakeFiles/hello.dir/main.cpp.o
/usr/bin/c++    -O3 -DNDEBUG   -o CMakeFiles/hello.dir/main.cpp.o -c /tmp/cmaketest/main.cpp
[100%] Building CXX object CMakeFiles/hello.dir/foo.cpp.o
/usr/bin/c++    -O3 -DNDEBUG   -o CMakeFiles/hello.dir/foo.cpp.o -c /tmp/cmaketest/foo.cpp
Linking CXX executable hello
/usr/bin/c++   -O3 -DNDEBUG   CMakeFiles/hello.dir/main.cpp.o CMakeFiles/hello.dir/foo.cpp.o
 -o hello -rdynamic
[...]

Much better than before. And if you find uncomfortable to have to edit the CMakeLists.txt file to switch between Release and Debug mode, you can also specify the option in the CMake command line, like this:

mkdir build && cd build
cmake -D CMAKE_BUILD_TYPE=Debug ../
make

Last, if you are a GCC wizard and you want full control of the options passed to the compiler, you can also set them manually. However, keep in mind that a CMakeLists.txt file should ideally work with many compilers. So before forcing compiler options, you need to check that the compiler is really GCC. This is an example that shows how to do it:

cmake_minimum_required(VERSION 2.6)
project(HELLO)

## Target
set(HELLO_SRCS main.cpp foo.cpp)
add_executable(hello ${HELLO_SRCS})

## Compiler flags
if(CMAKE_COMPILER_IS_GNUCXX)
    set(CMAKE_CXX_FLAGS "-O2")        ## Optimize
    set(CMAKE_EXE_LINKER_FLAGS "-s")  ## Strip binary
endif()

Note that you must not specify a build type (Debug or Release) since it apparently conflicts with the manually set compiler flags.

Last note, not necessarily CMake related, if you happen to have a multicore CPU and want to speed up builds, you can use the -jX option of make, where X is the number of cores in your CPU. It tells make to compile X files (lol :D ) at the same time. So for a dual core, use:

mkdir build && cd build
cmake ../
make -j2