Chapter 3

Cmake CACHE

Basic Syntax With Cache Example

Continuing the example from the previous chapter, lets imagine that this time we have a simple test file (in this case we are not going to use any test framwework, instad we are going to create a simple file that emulates testing) which is build when a variable exists inside the scope of our CMakeLists file otherwise the excutable is not created. The objective of this exercise is the introduction of cache variables inside CMake.

First lets build our HelloTest.cpp file which only objective is to check that the program we built returns correctly the string "hello world".

//
// HelloTest.cpp
// 
#include "Hello.h"
#include <assert.h>
#include <string.h>

int main(int argc, char** argv) {

    assert(std::string("Hello World").compare(helloWorld()) == 0);
    return 0;
}

The next file we are going to modify is our CMakeLists.txt. This time we need a conditional branch which will be executed only when we are in DEBUG_MODE.

# CMakeLists.txt

# We are using version 3.0.2 of CMake for this example
cmake_minimum_required( VERSION 3.0.2 )

# Workspace is HelloWorld, at the end you should see a 
# HelloWorld directory witht the project on it
project( HelloWorld )

set( HELLO_HEADERS Hello.h )
set( HELLO_SRCS Hello.cpp )

# Link the sources with the final binary. The name of the binary
# is going to be "hello"
add_executable( hello ${HELLO_SRCS} ${HELLO_HEADERS} )

# If the flag DEBUG_MODE is set, build the test file
if( DEBUG_MODE )

    set( HELLO_TEST_SRCS HelloTest.cpp )
    add_executable( hello_test ${HELLO_TEST_SRCS} ${HELLO_HEADERS} )

endif()

In this CMakeLists we have a new variable which is never set inside the file DEBUG_MODE.

When CMake finds a vaiable in the file it follows the following process:

  1. Search in the local scope if a variable with the same name (case insensitive) has been set previously
  2. Search one scope above in the CACHE to search for such a variable. If the variable is not found in the CACHE create a new one with the same value and a default value of '' and continue processing the file.

It's important to make emphasis in the fact that the variable is created inside the CACHE with a default value of an empty string, because this could lead some errors if you think the variable doesn't exists.

Now we can run our program with the following program:

$ cmake -DDEBUG_MODE:BOOL=ON CMakeLists.txt

This time we have a new flag (-D) which allows the user to initialize CACHE variables from the command line. The format follow by the flag is

-D<cache variable name>:<cache variable type>=<cache variable value>

Where in our previous command we have:

cache variable name -> DEBUG_MODE
cache variable type -> BOOL
cache variable value -> ON

The output from our terminal shouldn't be different than from the previous time

-- The C compiler identification is GNU <C compiler version>
-- The CXX compiler identification is GNU <Cpp compiler version>
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: <Path where the build files were generated>

And the reason why is not different is because fundamentally nothing has changed from our previous exercise.

Finally we build our project using the command

cmake --build .

Should generate the following output

Scanning dependencies of target hello
[ 50%] Building CXX object CMakeFiles/hello.dir/Hello.cpp.o
Linking CXX executable hello
[ 50%] Built target hello
Scanning dependencies of target hello_test
[100%] Building CXX object CMakeFiles/hello_test.dir/HelloTest.cpp.o
Linking CXX executable hello_test
[100%] Built target hello_test

As you may notice this time the new executable hello_test was generated, to prove that it's generated only when the flag is active you could delete all the new files generated by CMake and try to build the project without initializing DEBUG_MODE, the output should be

Scanning dependencies of target hello
[100%] Building CXX object CMakeFiles/hello.dir/Hello.cpp.o
Linking CXX executable hello
[100%] Built target hello

The previous examples displayed one of the basic features of CMake, the CACHE variables. Although, in this simple example we may not be able to appreciate the usefulness of such feature as it will be easier to just setup the variable inside the file based on some environmental variable of our operating system.

CACHE variable become more useful as the size of projects grow and the environments where we need to build our project diversify.

CMakeCache.txt

Every new project built with CMake, has multiple conditions and constant which are shared thorugh the entire project. This shared values are generated and configured when we execute command

cmake CMakeLists.txt

On this step of the process CMake creates a new file called CMakeCahe.txt, which contains all the shared CACHE variables that the project needs to be built. The format this varibles are written inside the file is the same format we used in the command line flag

<cache variable name>:<cache variable type>=<cache variable value>

For example if you open the file CMakeCahe.txt after runing the cmake command with the flag -DDEBUG_MODE:BOOL=ON you should notice that the flag is in the file. In my computer it looks:

$ cat CMakeCache.txt | grep DEBUG_MODE
DEBUG_MODE:BOOL=ON

All the variables inside this file are used in the next step to finally build the project and to generate the binaries.

Note: You can modify CMakeCache.txt file before running 'cmake --build' although is not recommended, the syntax of the CACHE variables is subject to change, and the files have full paths that make them unsiitable to move between build trees or machines. If you need to change the value and don't want to use the command line, refer to the section CACHE variables from files

There are other three variables that were generated based on the values we put in our CMakeLists and which are computed when the file is read the first time:

$ cat CMakeCache.txt | grep Hello
CMAKE_PROJECT_NAME:STATIC=HelloWorld
HelloWorld_BINARY_DIR:STATIC=<path where the binary will be generated>
HelloWorld_SOURCE_DIR:STATIC=<path to the source directory>

At the same time there exist other variables that were generated, all of them have comments on top of them that describe their current functionality and you can find a list of them in the Appendix A.

CACHE variables from files

When the number of cache variables increases or the number of build trees expand, initializing them inlined in the command line can be prone to errors and expensive in productivity time. In that case the CMake CACHE variables can be initialized using an specialized file.

The file is loaded using the flag '-C' of the CMake command and the syntax of the file is typically just a series of set commands. For example if we wanted that our DEBUG_MODE variable to be loaded from a file, then we'll create a new file with the following content:

# CMakeValuesCache.txt The name of the file is not important
set( DEBUG_MODE ON CACHE BOOL "Debug mode flag" )

This would add the variable DEBUG_MODE of type BOOL into the CACHE with a value of ON and the description "Debug mode flag". The syntax to add a new variable into cache is the following:

set( <variable name> <variable value> CACHE <variable type> <description> )

To use the new flag the command would be:

cmake -C CMakeValuesCache.txt CMakeLists.txt

Which should give the same output as previously

-- The C compiler identification is GNU <C compiler version>
-- The CXX compiler identification is GNU <Cpp compiler version>
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: <Path where the build files were generated>

Inside the CmakeCache.txt you'll find the next to lines

$ cat CMakeCache.txt | grep -A1 "Debug mode flag"
//Debug mode flag
DEBUG_MODE:BOOL=ON

As you can see the comment on the top is the descripton that we give to the variable inside the file, followed by the variable been set to ON.

In some cases there might be an existing cache and you want to force the CACHE value to a specific value. For example you want to set DEBUG_MODE even if the environment doesn't allow it. Then you can use the FORCE option at the end of the arguments in the set command:

set( <variable name> <variable value> CACHE <variable type> <description> FORCE )

results matching ""

    No results matching ""