Introduction to CMake for C++

Getting started with CMake for building C++ applications.


UPDATE:

This text is outdated and the advice given here should no longer be considered good practices. It’s being deprecated in favour of a much better style known as “Modern CMake”, which has plenty of advantages over the way to use CMake shown here. Please, consider the following links for introductions on Modern CMake:

This article will be kept available, since it’s part of the CMake’s history, in particular my of my path learning CMake, and it may also be useful for showing the advantages of Modern CMake. Moreover, it may help developers that are working on pre-Modern CMake projects. However, please do consider applying Modern CMake techniques instead.

Build System

CMake is a cross-platform tool for building software projects.

Normally, a build tool like GNU Make will parse a configuration file (Makefile), which describes the commands required to build an artefact based on the source files and other resources that make up the project. CMake is not so different, it also parses a similar configuration file (CMakeLists.txt), however, instead of directly build the artefact, it generates another configuration file that will then be used build the artefact.

That’s another level of indirection, which is a fairly popular wat to solve problems in Computer Science.

CMake takes advantage of it by the following:

By using only one configuration file, you can generate multiple configuration files to build your project across multiple platforms (Make, Visual Studio, etc).

In other words, that makes the build process portable across platforms.

The configuration file generated from the CMakeLists.txt will be forwarded to the native build system (a.k.a generator) that will then build the project. CMake comes with plenty of generators, such as Make, Visual Studio, etc.

More than portability, by using CMake you can considerably simplify your building process for projects composed of files spread over multiple sub- directories, e.g. src for .cpp, includes for .hpp, build for generated artefacts, etc.

Example

To illustrate the basic structure of a CMake project, let’s write an example.

It will consist of a C++ application written in Linux named rvarago-hello-cmake that shall be compiled against the C++14 standard with g++. The application is composed of three files: person.hpp, person.cpp, and main.cpp. The final artefact will be the executable hello.

The native build system will be Make, so CMake will generate a Makefile based on a CMakeLists.txt.

The project is structured in the following layout (output of the tree utility in Linux):

CMake structure

  • build is empty now, but it’ll contain the generated Makefile, executable, and other resources from the building process. The main reason to have this directory is that we can clean up all the stuff generated by CMake simply by removing the contents of this directory.
  • include is for the interfaces (.hpp).
  • src for the implementations (.cpp).

person.hpp is the interface-file for the class Person:

person.cpp is its implementation-file:

main.cpp has the main function, and it uses Person, by depending on person.hpp:

We can now compile our project with the following command:

g++ -o build/hello src/person.cpp src/main.cpp -I./includes -std=c++14

And we should end up with the hello executable inside the build directory.

It’s also possible to write a Makefile to use Make to conditionally compile the project. However, as I said, it won’t super simple, especially because we’re dealing with files spread over multiple directories and we might want to build our project on different operating systems without much fiddling with the build system. That’s when CMake really shines.

To build our application with CMake we may write CMakeLists.txt similar to:

Change your working directory to build, and instruct cmake to generate the build-files to be consumed by the native build system (GNU Make in my case), by supplying the path to the CMakeLists.txt, and it’ll generate a bunch of files current directory, that is, build:

cmake ..

Now, we have the Makefile generated from CMakeLists.txt, and to finally compile our application, we can invoke make:

make

And we should see the hello executable inside the build directory.

Conclusion

We’ve discussed the basics of how to get started with CMake to build C++ applications. We wrote a a simple example to compile a multi-file application with CMake.

References

[1] https://cmake.org/


Originally published at https://medium.com/@rvarago — layout: “post” title: “Introduction to CMake for C++” —

Getting started with CMake for building C++ applications.


UPDATE:

This text is outdated and the advice given here should no longer be considered good practices. It’s being deprecated in favour of a much better style known as “Modern CMake”, which has plenty of advantages over the way to use CMake shown here. Please, consider the following links for introductions on Modern CMake:

This article will be kept available, since it’s part of the CMake’s history, in particular my of my path learning CMake, and it may also be useful for showing the advantages of Modern CMake. Moreover, it may help developers that are working on pre-Modern CMake projects. However, please do consider applying Modern CMake techniques instead.

Build System

CMake is a cross-platform tool for building software projects.

Normally, a build tool like GNU Make will parse a configuration file (Makefile), which describes the commands required to build an artefact based on the source files and other resources that make up the project. CMake is not so different, it also parses a similar configuration file (CMakeLists.txt), however, instead of directly build the artefact, it generates another configuration file that will then be used build the artefact.

That’s another level of indirection, which is a fairly popular wat to solve problems in Computer Science.

CMake takes advantage of it by the following:

By using only one configuration file, you can generate multiple configuration files to build your project across multiple platforms (Make, Visual Studio, etc).

In other words, that makes the build process portable across platforms.

The configuration file generated from the CMakeLists.txt will be forwarded to the native build system (a.k.a generator) that will then build the project. CMake comes with plenty of generators, such as Make, Visual Studio, etc.

More than portability, by using CMake you can considerably simplify your building process for projects composed of files spread over multiple sub- directories, e.g. src for .cpp, includes for .hpp, build for generated artefacts, etc.

Example

To illustrate the basic structure of a CMake project, let’s write an example.

It will consist of a C++ application written in Linux named rvarago-hello-cmake that shall be compiled against the C++14 standard with g++. The application is composed of three files: person.hpp, person.cpp, and main.cpp. The final artefact will be the executable hello.

The native build system will be Make, so CMake will generate a Makefile based on a CMakeLists.txt.

The project is structured in the following layout (output of the tree utility in Linux):

CMake structure

  • build is empty now, but it’ll contain the generated Makefile, executable, and other resources from the building process. The main reason to have this directory is that we can clean up all the stuff generated by CMake simply by removing the contents of this directory.
  • include is for the interfaces (.hpp).
  • src for the implementations (.cpp).

person.hpp is the interface-file for the class Person:

person.cpp is its implementation-file:

main.cpp has the main function, and it uses Person, by depending on person.hpp:

We can now compile our project with the following command:

g++ -o build/hello src/person.cpp src/main.cpp -I./includes -std=c++14

And we should end up with the hello executable inside the build directory.

It’s also possible to write a Makefile to use Make to conditionally compile the project, but it won’t be a simple task, because we’re dealing with files spread over multiple directories and porting into different operating systems might not be trivial. That’s when CMake really shines.

To build our application with CMake we may write CMakeLists.txt similar to:

Change your working directory to build, and instruct cmake to generate the build-files to be consumed by the native build system (GNU Make in my case), by supplying the path to the CMakeLists.txt, and it’ll generate a bunch of files current directory, that is, build:

cmake ..

Now, we have the Makefile generated from CMakeLists.txt, and to finally compile our application, we can invoke make:

make

And we should see the hello executable inside the build directory.

Conclusion

We’ve discussed the basics of how to get started with CMake to build C++ applications. We wrote a a simple example to compile a multi-file application with CMake.

References

[1] https://cmake.org/


Originally published at https://medium.com/@rvarago

Share: X (Twitter) Facebook LinkedIn