How to use CMake

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

 
comments powered by Disqus