Today I started working on an existing CUDA project and the first thing I did was to try to build the project. However, there were some problems with the CMake system which is used in the project. Therefore, I decided to learn more about CMake and fix the problem. I would like to share what I’ve learnt here in this blog post. Specifically, I wanna talk about how CMake should be used, which can be a bit confusing for someone using CMake for the first time.
Why not Make?
If you know what Makefile is, you may wonder why someone would use CMake when there is already Make. Well, I do agree that Make can be quite handy if you are working on a small codebase which doesn’t have a lot of logic in building the project. In that case, using CMake is not really necessary. Also, Make is useful for some simple automation tasks. However, if you have a large project with somewhat complex compilation steps, CMake will be a better tool. Specifically, CMake lets you easily handle things like building on multiple platform, supporting multiple compilers, supporting optional dependencies, etc. As our focus for this post is on how to use CMake, details on how to write CMake files to accomplish all these are not discussed here.
How to use CMake
So how exactly do we use CMake? Well, first you have to make sure CMake is installed. If it’s not, it could be easily installed on Linux or macOS using package managers. For example, you would install CMake on Ubuntu with
$ apt-get install cmake
Now suppose you have a project with CMake as the build system, you need to do the following
$ mkdir /path/to/build/directory # you could create the directory at anywhere you like $ cd /path/to/build/directory $ cmake /path/to/project/directory $ make
Notice that we have a separate folder for the build. This is a good practice as it won’t mess with your source folder and you could also build multiple variants in multiple folders. When you run the
cmake command, it reads a file called
/path/to/project/directory and generates a lot of files in
/path/to/build/directory according to the settings in
CMakeLists.txt (including the
Makefile). In the simplest case, this is all you need to do to build a project. However, usually you need some tweaking before invoking
make. The next section discusses how to tweak the configuration.
Tweaking project configurations
One of the files that CMake generates is the
CMakeCache.txt file. This file contains the configurations used for a particular project. For example, it stores the value of
CMAKE_INSTALL_PREFIX which specifies which directory to install the binaries/libraries to when using
make install later. When you run
cmake for the first time, the content of this file is generated according to
CMakeLists.txt. There are basically three ways that you could use to tweak the configurations.
Use CMake with command line options
Let’s use the configuration
CMAKE_INSTALL_PREFIX as an example. If you run
cmake, the default value for it on Linux would be
/usr/local. Now if you want to change the value of it, you need to run
cmake with an additional argument
$ cmake -DCMAKE_INSTALL_PREFIX=/some/other/install/directory /path/to/project/directory
If you check
CMakeCache.txt after running CMake with that additional argument, you will see that the value of
CMAKE_INSTALL_PREFIX was set to
/some/other/install/directory. For any variables that you wanna change, you could use this method (adding
-D in front).
CMakeCache.txt file stores the configurations, another way to change the configurations is to edit the file directly. After saving the changes, rerun CMake so that it picks up the changes you made. Please note that this is not the recommended way to change configurations. The reason is that as you change the configurations, new configurations may be added to the
CMakeCache.txt file. It’s just not easy to keep track of all your configurations over multiple runs of CMake.
Use the GUI tool
On Linux, CMake usually comes with a companion tool called
ccmake. Install it if it doesn’t come with CMake on your system. This is a terminal GUI-like application that helps you with tweaking CMake configurations. In order to use it, do
$ $ mkdir /path/to/build/directory # you could create the directory at anywhere you like $ cd /path/to/build/directory $ ccmake /path/to/project/directory
A GUI-like interface will appear. Hit the key
C once, it will run
cmake and all the default settings in
CMakeCache.txt will show up. Navigate through different variables using arrow keys and change the values by using
Enter (and hit
Enter again after change). After changing some values, hit
C again to rerun
cmake. When you are happy with all configurations, hit
G to generate the Makefile and exit
Multiple runs of CMake
In the past, I always wonder what the differences between the first run of CMake and all subsequent runs are. Today, I finally figured it out and it’s actually quite simple. When you run CMake for the first time, you supply the source directory as the argument. CMake will read the
CMakeLists.txt file and generate a “build” in your current directory. For all subsequent runs of CMake, you can just supply the build directory to CMake. CMake will remember where the source directory is and run
CMakeLists.txt there again. Meanwhile, all the variables in
CMakeCache.txt are defined in the running environment before instructions in
CMakeLists.txt are executed. This is why you can tweak the configurations by changing the
CMakeLists.txt is always executed and
CMakeCache.txt is used if it’s available (i.e. not the first run).
In this post, I described how to run CMake and how to tweak CMake configurations for a project. Also, I mentioned what is done under the hood during every
cmake run as I think it might be confusing to some people (certainly to me at first). I hope you understand how to use CMake after you read this post.
- How to understand the difference between two command line options for cmake?
- Running CMake
- What is an “out-of-source” build?