Using configuration options in c++ using CMake
Sometimes when writing generic code you want to only using certain bits
of code in special circumstances, for example only use OpenCV
code
when it is availble. With CMake
this is easy.
CMake
hello world!
Here is our hello-world.cc
code
#include <iostream>
int main()
{
std::cout << "hello world" << std::endl;
return 0;
}
and simple CMakeLists.txt
project( hello )
set (hello_VERSION_MAJOR 1)
set (hello_VERSION_MINOR 0)
add_executable( hello hello-world.cc )
config.h
The fist step is to generate a config.h
file. This file can be
automatically be generated by CMake
and then included into your c++
code. As a simple example, this is an example of config.h.in
which
CMake
will convert into config.h
for us:
// the configured options and settings for hello
#define hello_VERSION_MAJOR @hello_VERSION_MAJOR@
#define hello_VERSION_MINOR @hello_VERSION_MINOR@
To tell CMake
to generate the config.h
for us, we update our
CMakeLists.txt
file:
# configure a header file to pass some of the CMake settings
# to the source code
project( hello )
set (hello_VERSION_MAJOR 1)
set (hello_VERSION_MINOR 0)
# tell CMake to generate config.h
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")
add_executable( hello hello-world.cc )
Note that we save the config.h.in
file in our source directory and the
generated config.h
is in the build directory. This means that this
file is set a configure time and several out-of-source builds can have
different config.h
files. We must also include the project build
directory so that the new file can be included.
We can update hello-world.cc
to make use of our new configuration
details:
#include "config.h"
#include <iostream>
int main()
{
std::cout << "hello world" << std::endl;
std::cout << "hello version " << hello_VERSION_MAJOR << "."
<< hello_VERSION_MINOR << std::endl;
return 0;
}
The result of calling cmake
is a file in the build directory called
config.h
containing:
#define hello_VERSION_MAJOR 1
#define hello_VERSION_MINOR 0
Compiling and running the code gives:
$ ./hello
hello world
hello version 1.0
#cmakedefine
Often CMake
is used in order to test whether various libraries exist
on the system using the find_package
mechanism. We can take advantage
of this feature and compiler directives in order to change which parts
of our code are compiled. This might be useful for example to only use
code which depends on a library if that library is found!
In our simple example, we will simply use two variables YES
and NO
which will be set to true or false.
# configure a header file to pass some of the CMake settings
# to the source code
project( hello )
set (hello_VERSION_MAJOR 1)
set (hello_VERSION_MINOR 0)
set (YES True)
set (NO False)
# tell CMake to generate config.h
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")
add_executable( hello hello-world.cc )
This is incorporated into the config.h.in
file using the
#cmakedefine
directive
#define hello_VERSION_MAJOR @hello_VERSION_MAJOR@
#define hello_VERSION_MINOR @hello_VERSION_MINOR@
#cmakedefine YES
#cmakedefine NO
Calling cmake
results in an update config.h
file:
#define hello_VERSION_MAJOR 1
#define hello_VERSION_MINOR 0
#define YES
/* #undef NO */
cmake
has substituted the directive with either a #define
directive
or a commented out #undef
directive.
This can be used in our hello-world.cc
code as:
#include "config.h"
#include <iostream>
int main()
{
std::cout << "hello world" << std::endl;
std::cout << "hello version " << hello_VERSION_MAJOR << "."
<< hello_VERSION_MINOR << std::endl;
#ifdef YES
std::cout << "YES is true" << std::endl;
#endif
#ifdef NO
std::cout << "NO is true" << std::endl;
#endif
return 0;
}
Compiling and running the code gives:
$ ./hello
hello world
hello version 1.0
YES is true
See also
This post is based on this Cmake
tutorial which gives lots of other
options of how to build a clever CMakeLists.txt
too.