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 CMakeLists.txt
from /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).
Change CMakeCache.txt
directly
Since the 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 ccmake
.
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 CMakeCache.txt
file.
In summary, CMakeLists.txt
is always executed and CMakeCache.txt
is used if it’s available (i.e. not the first run).
Conclusion
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.
References
- How to understand the difference between two command line options for cmake?
- Running CMake
- What is an “out-of-source” build?