Using CMake to set preprocessor directives

Preprocessor flags

C++ preprocessor directives are useful for many things. One use is to set flags from the command line in order to compile the program in different ways. For instance, we could have a DEBUG flag which, if set, gives us some extra feedback useful for debugging:

int main() {
    // Do some stuff...
    // If in debug mode, output some extra info:
    #ifdef DEBUG
        std::cout << NAtoms << "\t" << energy << std::endl;
    #endif
}

These options could be set from the command line when compiling the program, by prefixing their name with -D:

g++ main.cpp -o main -DDEBUG

This is the equivalent of setting #define DEBUG at the beginning of main.cpp. You can also give it a value:

g++ main.cpp -o main -DDEBUG=1

Is equivalent to writing #define DEBUG 1 in main.cpp.

Setting flags with CMake

If you use CMake however, you’re not writing the compilation command yourself. So, how can we achieve the same effect?

First, we will use CMake’s option() function to add a command-line option when running the cmake command:

option(USE_DEBUG "Enter debug mode" OFF)

Now we can set this option to ON when running CMake:

cmake -DUSE_DEBUG=ON ..

Then, we’ll use CMake’s add_definitions() function to set the corresponding preprocessor flag in our C++ program:

if (USE_DEBUG)
  add_definitions(-DDEBUG)
endif()

That’s all there is to it. Below you can find a sample CMake file with this functionality built into it:

# Minimum version of CMake required:
cmake_minimum_required(VERSION 3.1)

# A name for our project:
project(demo_cmake)

# This allows us to enable C++11 for compilation on all targets.
# Possible values: 98, 11, 14
set(CMAKE_CXX_STANDARD 11)

# This allows us to add flags to all compiler calls
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Wextra")

# Add debugging option
option(USE_DEBUG "Enter debug mode" OFF)
if (USE_DEBUG)
  add_definitions(-DDEBUG)
endif()

# Now tell CMake we want to compile a file "main" from "main.cpp"
add_executable(main main.cpp)

# Tell CMake to compile our library as "mylib"
add_library(mylib src/mylib1.cpp src/mylib2.cpp src/mylib3.cpp)

# Make sure the compiler can find include files for our library
#  when other libraries or executables link to mylib
target_include_directories(mylib PUBLIC ${CMAKE_SOURCE_DIR}/src/)

# Now tell CMake that our file "main" requires the library "mylib"
target_link_libraries(main mylib)