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):
build
is empty now, but it’ll contain the generatedMakefile
, 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
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):
build
is empty now, but it’ll contain the generatedMakefile
, 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
Originally published at https://medium.com/@rvarago