Step 12: Packaging Debug and Release¶
Note: This example is valid for single-configuration generators and will not work for multi-configuration generators (e.g. Visual Studio).
By default, CMake’s model is that a build directory only contains a single configuration, be it Debug, Release, MinSizeRel, or RelWithDebInfo. It is possible, however, to setup CPack to bundle multiple build directories and construct a package that contains multiple configurations of the same project.
First, we want to ensure that the debug and release builds use different names for the libraries that will be installed. Let’s use d as the postfix for the debug libraries.
Set CMAKE_DEBUG_POSTFIX near the beginning of the top-level CMakeLists.txt file:
set(CMAKE_DEBUG_POSTFIX d) add_library(tutorial_compiler_flags INTERFACE)
And the DEBUG_POSTFIX property on the tutorial executable:
add_executable(Tutorial tutorial.cxx) set_target_properties(Tutorial PROPERTIES DEBUG_POSTFIX $CMAKE_DEBUG_POSTFIX>) target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
Let’s also add version numbering to the MathFunctions library. In MathFunctions/CMakeLists.txt , set the VERSION and SOVERSION properties:
set_property(TARGET MathFunctions PROPERTY VERSION "1.0.0") set_property(TARGET MathFunctions PROPERTY SOVERSION "1")
From the Step12 directory, create debug and release subdirectories. The layout will look like:
Now we need to setup debug and release builds. We can use CMAKE_BUILD_TYPE to set the configuration type:
cd debug cmake -DCMAKE_BUILD_TYPE=Debug .. cmake --build . cd ../release cmake -DCMAKE_BUILD_TYPE=Release .. cmake --build .
Now that both the debug and release builds are complete, we can use a custom configuration file to package both builds into a single release. In the Step12 directory, create a file called MultiCPackConfig.cmake . In this file, first include the default configuration file that was created by the cmake executable.
Next, use the CPACK_INSTALL_CMAKE_PROJECTS variable to specify which projects to install. In this case, we want to install both debug and release.
include("release/CPackConfig.cmake") set(CPACK_INSTALL_CMAKE_PROJECTS "debug;Tutorial;ALL;/" "release;Tutorial;ALL;/" )
From the Step12 directory, run cpack specifying our custom configuration file with the config option:
cpack --config MultiCPackConfig.cmake
CMake part 2: Examples to build executable and library projects
CMake is a cross-platform software for building projects written in C, C++, Fortran, CUDA and so on. CMake utilizes build-systems such as Ninja, Linux make, Visual Studio, and Xcode. It compiles projects with compilers like GCC, Clang, Intel, MS Visual C++.
CMake is frequently used in compiling open-source and commercial projects. It has comprehensive but daunting manual instruction. In this post, instead of throwing instructions for some random commands, I aim to explain how to employ modern CMake step by step to build executables (applications) and static/shared/header-only libraries from C++ projects.
Prerequisits
- you had a look at my post on CMake programming,
- you have CMake v3.23 on your machine,
- you have a compiler like GCC, Clang, Intel, or MS Visual C++ installed on your operating system.
Compile examples
Examples are on GitHub here and their links are mentioned in each section as well. To build an example, go to its directory in a terminal and run
Usual build configurations are Debug, Release, RelWithDebInfo and MinSizeRel. For single configuration generators like make and Ninja run:
For multi-configuration generators like Visual Studio or Xcode, the configuration can be set at the building stage:
cmake .. cmake --build . --config Release
To install targets, after building the project, run CMake like
cmake --install . --prefix --config Release
Note that —config Release only works for multi-configuration generators. For a single configuration generator, you already set the CMAKE_BUILD_TYPE before building the project.
Basic executable
The code for this example is here on GitHub. It is a simple executable (or application). The project folder is like this:
---example1 | ----- app.cpp | ----- shape.cpp | ----- shape.h | ----- CMakeLists.txt
app.cpp includes shape.h . The CMakeLists.txt is
Executable with subdirectories
The code for this example is here on GitHub.
In this example, we have source files and headers distributed in subdirectories:
--- geometry | ---- shape | | | --- shape.h | | | --- shape.cpp | | | --- CMakeLists.txt | ---- square | | | --- square.h | | | --- square.cpp | | | --- CMakeLists.txt | ---- app.cpp | ---- CMakeLists.txt
Where geometries/CMakeLists.txt is
shape/CMakeLists.txt is just
and square/CMakeLists.txt is
So we added sources in the subdirectories to app target.
Executable with namespace
The code for this example is here on GitHub.
This example is similar to a big project with namespaces. Namespaces are used to avoid name conflicts in a project, read more on them in this post. The CMake script is very similar to the previous example.
The project folder is like
--- geometry | ---- shape | | | --- base.h, base.cpp | | | --- CMakeLists.txt | ---- rectangle | | | --- base.h, base.cpp | | | --- CMakeLists.txt | ---- square | | | --- base.h, base.cpp | | | --- CMakeLists.txt | ---- app.cpp | ---- CMakeLists.txt
base files represent base class for different shapes. It is assumed the developer will derived more classes from them. We have files and classes with the same name, but they are elegently resolved with namespaces and CMake.
CMakeLists.txt in shape, rectangle and square are the same:
The code for this example is here on GitHub.
In this example, we compile a library and link it to an executable
--- geometry | ---- shape | | | --- shape.h, shape.cpp | | | --- CMakeLists.txt | ---- square | | | --- square.h, square.cpp, info.h | | | --- CMakeLists.txt | ---- example | | | --- app.cpp | ---- CMakeLists.txt
The geometries/CMakeLists.txt is
Therefore, in the geometry/CMakeLists.txt , this line
means that CMake installs the public headers in the include directory with their relative path, like install/path/include/square/square.h .
Header-only library
The code for this example is here on GitHub.
A header-only library has all the implementations defined in headers. There are .h / .hpp files and but no .cpp files except for tests. This type of library is not compiled standalone. But other projects link to them. Therefore, when a header-only library is installed, only header files are copied at the destination.
The example for this case has the below structure:
--- geometry | ---- shape | | | --- shape.h | | | --- CMakeLists.txt | ---- square | | | --- square.h | | | --- CMakeLists.txt | ---- examples | | | --- example1.cpp | --- geometry.h | ---- CMakeLists.txt
The example1.cpp is like this
The geometry/CMakeLists.txt is
and square/CMakeLists.txt is
My next post is on creating config files in CMake to find package. And in case you haven’t seen it, my previous post was on programming in CMake.
Even more
Designing a big project needs a good understanding of namespaces, see my post on how namespaces are used in big projects.
Did you know CMake is supported by Visual Studio code? have a look at my essential list of VS code extensions for C++.
Latest Posts
Debug vs Release in CMake
With CMake, it’s generally recommended to do an «out of source» build. Create your CMakeLists.txt in the root of your project. Then from the root of your project:
mkdir Release cd Release cmake -DCMAKE_BUILD_TYPE=Release .. make
And for Debug (again from the root of your project):
mkdir Debug cd Debug cmake -DCMAKE_BUILD_TYPE=Debug .. make
Release / Debug will add the appropriate flags for your compiler. There are also RelWithDebInfo and MinSizeRel build configurations.
You can modify/add to the flags by specifying a toolchain file in which you can add CMAKE__FLAGS__INIT variables, e.g.:
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-Wall") set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wall")
As for your third question, I’m not sure what you are asking exactly. CMake should automatically detect and use the compiler appropriate for your different source files.
You can also do a cmake -i .. instead, so cmake will run interactively, asking you which type of build you want (None, Release, Debug, MinSizeRel, RelWithDebInfo).
@thiagowfx -i option results in this error message: The «cmake -i» wizard mode is no longer supported. . I’m using cmake 3.7.1
This is NOT an out of source build if you are creating a sub-directory! It is advised to create the build directory outside/above the source directory.
I have to run cmake —build . —config Release to get release build. If you omit —config Release you always get debug build, regardless of cmake -DCMAKE_BUILD_TYPE=Release .. in the previous command. (cmake 3.21)
A lot of the answers here are out of date/bad. So I’m going to attempt to answer it better. Granted I’m answering this question in 2020, so it’s expected things would change.
How do I run CMake for each target type (debug/release)?
First off Debug/Release are called configurations in cmake (nitpick).
If you are using a single configuration generator (Ninja/Unix-Makefiles) you must specify the CMAKE_BUILD_TYPE.
# Configure the build cmake -S . -B build/ -D CMAKE_BUILD_TYPE=Debug # Actually build the binaries cmake --build build/ # Configure a release build cmake -S . -B build/ -D CMAKE_BUILD_TYPE=Release # Build release binaries cmake --build build/
For multi-configuration generators it’s slightly different (Ninja Multi-Config, Visual Studio)
# Configure the build cmake -S . -B build # Build debug binaries cmake --build build --config Debug # Build release binaries cmake --build build --config Release
If you are wondering why this is necessary it’s because cmake isn’t a build system. It’s a meta-build system (IE a build system that build’s build systems). This is basically the result of handling build systems that support multiple-configurations in 1 build. If you’d like a deeper understanding I’d suggest reading a bit about cmake in Craig Scott’s book «Professional CMake: A Practical Guide
How do I specify debug and release C/C++ flags using CMake?
The modern practice is to use target’s and properties.
add_library(foobar) # Add this compile definition for debug builds, this same logic works for # target_compile_options, target_link_options, etc. target_compile_definitions(foobar PRIVATE $: FOOBAR_DEBUG=1 > )
NOTE: How I’m using generator expressions to specify the configuration! Using CMAKE_BUILD_TYPE will result in bad builds for any multi-configuration generator!
Further more sometimes you need to set things globally and not just for one target. Use add_compile_definitions, add_compile_options, etc. Those functions support generator expressions. Don’t use old style cmake unless you have to (that path is a land of nightmares)
How do I express that the main executable will be compiled with g++ and one nested library with gcc?
Your last question really doesn’t make sense.