CMake part 2: Compiler flags

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
About these ads

Tags: ,

14 Responses to “CMake part 2: Compiler flags”

  1. Hans Says:

    Hey, thanks for that info about using make with multiple cores. I was searching for how to do that and found your page. Do you know if there’s a way to specify -jX in the CMakeLists.txt file?

    Thanks,
    Hans

  2. fedetft Says:

    Well, the CMake activity is to generate makefiles (or project files for various IDEs). The process of compiling files is still done by makefiles.
    The -jX is an option that must be passed to make’s command line, and as far as I know you can’t specify that option inside the makefile, so CMake would have no way to write a makefile “optimized for a multicore”, so I think you just need to pass -jX to make’s command line after you’ve run CMake.

    • Tobibobi Says:

      I have a way to do it quite neatly because I work on a machine with 12 cores and therefore really can utilize the power the extra cores give me. However my neighbour have only a quad i7 core so he doesn’t have as many cores available to him.

      So what I do is use the MAKEFLAGS environment variable. If you set it like this in your .profilerc or .bashrc or whatever shell startup script you use, it will always be applied.

      export MAKEFLAGS=-j12 # bash style syntax

      Good luck

  3. PANDA Says:

    Thx for the post. It was very useful for me.

  4. Rafael Says:

    Thanks for the post.
    Exactly what i was looking for

  5. Alex Says:

    Sorry but the thing you are suggesting is somewhat weird.
    try adding multiple flags to your FLAGS

    like -Wall -Werror -std=c++0x you’ll quickly notice that multiple append will yield semicolon separate values. this is actually not what you want.

    instead you could use
    set(CMAKE_CXX_FLAGS “-Wall -O2″)
    which in turn overrides every previous value.

    set(CMAKE_CXX_FLAGS “-O2″) ## Optimize
    set(CMAKE_CXX_FLAGS “${CMAKE_CXX_FLAGS} -Wall”)

    This sets the value to -O2 and then appends -Wall.
    This is the behaviour I was looking for.

  6. fedetft Says:

    When adding more options I use this syntax:

    if(CMAKE_COMPILER_IS_GNUCXX)
    list(APPEND CMAKE_CXX_FLAGS “-O2 -Wall -Werror -std=c++0x”) ## Optimize
    list(APPEND CMAKE_EXE_LINKER_FLAGS -s) ## Strip binary
    endif()

    As you can see, I use append only once per each variable I modify.
    The reason I still use list append instead of set is to preserve any other option CMake might pass to the compiler.

  7. Alex Says:

    Well if it would preserve options passed by the compiler you could also use multiple list(APPEND ….) commands.

    CMake doesn’t use CMAKE_CXX_FLAGS as list.

  8. fedetft Says:

    I see your point, so I fixed the blog post.
    Thanks

  9. Jonathan Says:

    I know this is an old post, but the -j flag to make should be the number of cores/processors *plus one*.

    -j specifies the number of simultaneous jobs, not the number of simultaneous compiles, and an additional job is needed to tell the others what to do (which files to compile and in what order, and telling one or more jobs to wait for another if there are dependencies) if there is more than one. So -j2 is pointless, make spawns 2 jobs: the first compiles and the second sits on his rear (wasting a core) and tells the first to compile. -j3 is what you want on a dual-core: one compiler job for each core plus one job which tells the first 2 what to do.

  10. kjellkod Says:

    Great, exactly what I needed. Thank you!

  11. Zbyniowicz Says:

    This article was very helpful for me. Thanks a lot :)

  12. ilya Says:

    Scanning dependencies of target svn-crawler
    [100%] Building C object CMakeFiles/svn-crawler.dir/main.c.o
    In file included from /usr/include/subversion-1/svn_client.h:34:0,
    from /home/files/home_install/src/fastsvncrawler-code-3-trunk/main.c:1:
    /usr/include/apr-1.0/apr.h:358:1: error: unknown type name ‘off64_t’
    /home/files/home_install/src/fastsvncrawler-code-3-trunk/main.c: In function ‘crawl’:
    /home/files/home_install/src/fastsvncrawler-code-3-trunk/main.c:233:5: warning: ‘svn_ra_open3’ is deprecated (declared at /usr/include/subversion-1/svn_ra.h:632) [-Wdeprecated-declarations]
    make[2]: *** [CMakeFiles/svn-crawler.dir/main.c.o] Error 1
    make[1]: *** [CMakeFiles/svn-crawler.dir/all] Error 2
    make: *** [all] Error 2

    how can i fix this?

  13. fedetft Says:

    That’s not a CMake related error. You probably did not #include the file that contains the definition of off64_t

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

Join 30 other followers

%d bloggers like this: