How do I build a CMake project? - cmake

I have just acquired an arbitrary CMake project from the internet and I am not sure how to compile it. What commands do I need to run to build it from the command line?

Basic steps
If you're on a Unix-y operating system, like Linux or macOS, then you would run:
$ cmake -DCMAKE_BUILD_TYPE=Release -S /path/to/source-dir -B /path/to/build-dir
$ cmake --build /path/to/build-dir
Here, /path/to/source-dir is the directory containing the root-level CMakeLists.txt, this is most commonly the root of a source control repository. Meanwhile, /path/to/build-dir is a distinct directory (that does not need to exist yet) that CMake will use to store the generated build system and its outputs. This is called an out-of-tree build. You should never attempt an in-tree build with CMake because of the possibility of name clashes and difficulty involved with cleaning up the generated files.
When building with a single-config generator (like Make, which is the default on Unix), you specify the build type by setting the CMAKE_BUILD_TYPE variable in the first command, known as the configure step. You must always set this variable when working with a single-config generator. The built-in configs are Debug, Release, RelWithDebInfo, and MinSizeRel. See this answer for more detail on this.
After the configure step, you may build the project by either calling the underlying build tool (in this case, make) or by calling CMake's generic build launcher command (cmake --build), as I do here.
If you're on Windows, then the default generator is Visual Studio, which is a multi-config generator. This means the build type is chosen during the build step rather than the configure step, and the commands must be adjusted accordingly:
$ cmake -S /path/to/source-dir -B /path/to/build-dir
$ cmake --build /path/to/build-dir --config Release
These steps assume that the CMake build you are looking at is well behaved. If a project fails to build with the above steps and you have all of its dependencies installed to system locations (and they are well behaved), then you should open an issue with the upstream project. The most common source of bad behavior in mature CMake builds is dependency handling. Too often you will have to read the build or its documentation to determine which variables need to be set (via -D, like we did with CMAKE_BUILD_TYPE above) for the project to find its dependencies.
Advanced topics
Setting options and cache variables
Some projects offer options to enable/disable tests, components, features, etc. These are typically done by writing entries to the CMake cache during the configure step. For example, a common way to disable building tests is to set BUILD_TESTING to NO at the command line:
$ cmake -S /path/to/source-dir -B /path/to/binary-dir [...] -DBUILD_TESTING=NO
This particular variable is a convention, but is not guaranteed to be honored. Check the project's documentation to see which options are available.
Selecting a generator and toolchain
When using the Visual Studio generators specifically, you can tell CMake which platform you wish to target and which version of the compiler you would like to use. The full form of the CMake configure command for this is:
$ cmake -G "Visual Studio 16 2019" -A <ARCH> -T<TOOLSET> [...]
Valid values of <ARCH> include Win32, x64, ARM, and ARM64. If <TOOLSET> is not specified, then the 32-bit MSVC compiler will be used. Typically, you will want this to be host=x64 to ensure that 64-bit MSVC is used, which can allocate more memory for large linking steps. You can also set <TOOLSET> to ClangCL to use the Visual Studio provided ClangCL tools.
On all generators, CMake sniffs the environment for which compiler to use. It checks the CC and CXX environment variables for the C and C++ compilers, respectively. If those are empty, it will look for cc and c++ executables in the PATH. You can manually override the compilers by setting the CMAKE_C_COMPILER and CMAKE_CXX_COMPILER CMake cache (not environment) variables at the CMake command line (using -D again).
Installing & using dependencies
Once a CMake project has been built, you may install it either systemwide or (preferably) to a local prefix by running:
$ cmake --install /path/to/build-dir --prefix /path/to/install-dir [--config Release]
Where --config is only required if a multi-config generator was used. Once installed to a local prefix, a project that depends on it may be configured by setting CMAKE_PREFIX_PATH to /path/to/install-dir.

Related

How can I make colcon work with a plain preset-based CMake project with multiple presets in parallel?

Prologue
I have a preset-based plain CMake project so that I can build and test it with cmake --preset $PRESET && cmake --build --preset $PRESET && ctest --preset $PRESET. Note that it nicely interacts with Microsoft's CMake Tools extension for Visual Studio Code, be it for building, testing, debugging and Intellisense.
Since I want to handle multiple presets in parallel, I set CMakePresets.json's binaryDir property to ${sourceDir}/build/${presetName}/.
Issue
I want to also build this plain CMake project with colcon. colcon build --cmake-args "--preset $PRESET" doesn't work, though, as it produces
WARNING:colcon.colcon_cmake.task.cmake.build:Could not build CMake package 'root_project_name' because the CMake cache has no 'CMAKE_PROJECT_NAME' variable
root_project_name being the argument to CMake's project() command in the top CMakeLists.txt.
How can I resolve this warning and the subsequent build failure?
Straightforward solution
Not setting CMakePresets.json's binaryDir property at all works fine with colcon, but doesn't allow for multiple preset builds in parallel.
Solution with multiple preset builds in parallel
The reason for this behavior is colcon-core's build verb's passing the build base directory (default: build) suffixed by the found package's name (here: root_project_name) to the colcon-cmake extension here.
The solution is to pass the correct build base to colcon (i.e. colcon build --build-base ./build/$PRESET/ --cmake-args "--preset $PRESET") and to adapt your CMakePresets.json's binaryDir property to ${sourceDir}/build/${presetName}/root_project_name/.
Note that this then works with colcon test as well, i.e. colcon test --build-base ./build/$PRESET/ --ctest-args "--preset $PRESET".

How to make a multi-config generator behave like a single-config generator in a project using ExternalProjects?

I am using CMake to build a cross-platform (e.g. Linux/Windows) application which uses also thirdparty libraries as external projects.
I'm aware of the fact that MSVC is a multi-configuration environment, hence
CMAKE_BUILD_TYPE is not used, instead it uses the CMAKE_CONFIGURATION_TYPES set to build every possible configuration.
I'm also aware of the fact that instead of providing a configuration at configuration type (e.g. cmake -DCMAKE_BUILD_TYPE ..) I need to provide the configuration at build type (cmake --build . --config Release)
See https://stackoverflow.com/questions/24460486/cmake-build-type-not-being-used-in-cmakelists-txt]
What I completely fail to understand is:
I don't want multi-configuration builds. I want to build either Debug or Release- but only one of them in the generated buildsystem. Can I achieve that with CMake + multi-config generator (like MSVC or Ninja Multi-Config) + ExternalProject projects? If yes, how?
You should be able to achieve this by just setting CMAKE_CONFIGURATION_TYPES to a single-entry-list on the command line (with the -D argument format) like "-DCMAKE_CONFIGURATION_TYPES=Debug" or "-DCMAKE_CONFIGURATION_TYPES=Release" when you configure the buildsystem, and configuring your calls to ExternalProject_Add of external projects that use CMake to also pass on that value of CMAKE_CONFIGURATION_TYPES to the external project, like:
ExternalProject_Add(
# ...
CMAKE_CACHE_ARGS
"-DCMAKE_CONFIGURATION_TYPES:STRING=${CMAKE_CONFIGURATION_TYPES}"
# ...
)

Proper way to determine generator for cmake project

I'm in a cmake build directory and want to build the project, but don't know if cmake was run with -G Unix\ Makefile or -G Ninja.
Now I know I can just be ignorant of that and use cmake --build ., but when I want to provide additional options cmake --build . -- SOMETHING I should know if I should provide gnumake or ninja options.
As possible solutions I found that I can just check the presence of a Makefile or build.ninja file. Or grep in the CMakeCache.txt for CMAKE_GENERATOR.
EDIT:
In a similar question here it is explained that values passed to cmake with -D can be queried with cmake -LA -N. But this doesn't list the -G parameter.
I am wondering if there is a more proper way (just like cmake -LA -N instead of grepping for variable values).
You've cited two possible solutions:
Check presence of build system files. Not a general solution, as it only works for some generators, but it may work in your case. This would not work for Visual Studio (for example, unless you parse the Visual Studio files themselves), as each generator version creates the same filenames.
Grep CMakeCache.txt for CMAKE_GENERATOR. Relies on platform specific tools (grep), and the location of the CMakeCache.txt. Both likely not a problem in most situations.
A slight modification to the second option, which makes it more portable, is to cache the CMAKE_GENERATOR in another variable:
set(USED_CMAKE_GENERATOR "${CMAKE_GENERATOR}" CACHE STRING "Expose CMAKE_GENERATOR" FORCE)
Then, when you use cmake -L, USED_CMAKE_GENERATOR will show up.

CMake workflow?

I am learning CMake and I am having problems in understanding its multi-step workflow. So far, my understanding is that you:
write a CMakeLists.txt
run cmake from a subdirectory to generate a build file (a Makefile, in my case)
run make
However, I don't understand how you should handle different targets (Release vs Debug). By running CMake in two different subdirectories?
Also, I don't understand why you would edit CMakeCache.txt (there is also a GUI tool for that). To emulate what you would accomplish with ./configure by passing different options?
You got it pretty much right. The write CMakeLists.txt > cmake > make sequence is correct.
Regarding different configurations (Debug vs. Release), you have to differentiate between multi-config generators (Visual Studio, XCode), and single-config generators (everything else). With the multi-config generators, you generate one buildsystem (e.g. solution file) which contains all configurations, and choosing between them happens at build time.
With single-config generators, different configurations are obtained by generating different buildsystems, that is, by running CMake multiple times in different directories (and with a different value of the CMAKE_BUILD_TYPE CMake variable).
So you'd do something like this:
> cd my_project/bld/debug
> cmake ../../src -DCMAKE_BUILD_TYPE=Debug
> cd ../release
> cmake ../../src -DCMAKE_BUILD_TYPE=Release
Regarding editing the cache (usually through CMake GUI or ccmake): you're right again, this largely corresponds to passing options to ./configure from AutoMake world. This would be the typical workflow with a freshly downloaded project (using CMake GUI):
Run CMake GUI, point it to the source directory (input) and binary directory (output) you want
Configure. This will fill the cache with project-specified defaults. If CMake cannot find some dependencies of the project automatically, this will end with an error.
Inspect the cache, change any values you don't like (such as compilation options), fill in any missing options (paths to libraries CMake couldn't find etc.)
Repeat steps 2 & 3 until you're satisfied with the project's setup.
Generate the buildsystem.
Exit CMake GUI and build using the appropriate build tool.
What #Angew said. Plus here's an image of the cmake-gui:
Also note that you install it (the CMake GUI) on Ubuntu with sudo apt install cmake-qt-gui, and you run it with cmake-gui.
Source: Where is the CMake GUI for Linux?
Here's my cmake-gui image:

Why -DCMAKE_EXPORT_COMPILE_COMMANDS does not create compile_commands.json file

My cmake --version is 2.8.12.2.
I configure my project build with these commands:
cmake ../klein/ -DBUILD_KLEIN_DEPS=1 -DCMAKE_EXPORT_COMPILE_COMMANDS=1
cmake ../klein/ -DBUILD_KLEIN_DEPS=1 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake ../klein/ -DBUILD_KLEIN_DEPS=1 -DCMAKE_EXPORT_COMPILE_COMMANDS=on
CMAKE_EXPORT_COMPILE_COMMANDS=1 cmake ../klein/ -DBUILD_KLEIN_DEPS=1
From a clean build, and from a directory with an existing successful build. And want to see the compiler_commands.json file, but it does not appear.
At which moment should it be created: after cmake, or after make command? Where should it be in ./, or in ../klein directory? My cmake does not say anything about this option while it always complains about unused build variables.
Should it work from in a "dirty" directory, where I've performed one successful build, or does it work only on a fresh run in an empty folder?
Edit:
I use a default generator "Unix Makefiles" on my ubuntu linux machine
Edit2:
I'm not an author of the project under the question (I just want to explore it with rtags which requires compile_commands.json file), and I'm not very familiar with CMake mechanics. However, the CMakeLists.txt is probably configured as a super-build (it indeed downloads and builds dependencies - like llvm, z3, ...), and it includes ExternalProject, however it also builds the project itself (klein) from sources. So it's a mix, as I would say.
Can you specify what generator your using? A quick scan of the cmake source from version 3.1.0 suggests that this command is still only available in the following 2 cases.
if(CMAKE_GENERATOR MATCHES "Unix Makefiles")
and
if(CMAKE_GENERATOR MATCHES "Ninja")
if you're using Visual Studio directly you're out of luck unless you want to add a patch to CMake. Otherwise, I know many Windows developers who've gone to Ninja. One advantage is that it's vastly faster than Visual Studio for building. If you are, in fact using Ninja or Unix Makefiles, then it's worth digging deeper.