Quick CMake Tutorial
Contents:
- Basics
- Project file reloading
- Specifying header search paths
- Compiler settings
- Setting language standard
- Changing build directory
- Adding build targets
- Build targets and CLion’s configurations
- Including libraries
- Using Boost
- Including sub-projects
- CMake Cache
Basics
CMake is a build system that uses a single script (defined in a CMakeLists.txt
file)
to generate appropriate platform-specific
build scripts. For example, on Windows, CMake can generate files targeting Microsoft Visual Studio and the MSBuild system, whereas
on other operating system it can target various MAKE variants.
The first command you typically encounter in the root CMakeLists.txt
file is the specification of the minimum required
CMake version, cmake_minimum_required(VERSION x.x.x)
. This command specifies the necessary minimum for the project to build.
When you create a new project in CLion, the IDE automatically generates the CMakeLists.txt
file and inserts the current
version of CMake (taking the value from the version of CMake that’s set to be used in CLion), so you don’t need to explicitly specify
it yourself.
By default, CLion expect a CMakeLists.txt file to be provided in the root directory of all project-related files.
This can be customized, though, by moving CMakeLists.txt
and changing the project root accordingly.
Following the minimum required version we have the project() directive which defines the name of the project. A project is simply a collection of files that are all somehow related. You declare the name of the project like so
project(MyProject)
CMake lets you create text variables. For this purpose, the set() command is used. For instance, to create a
variable MY_VAR
with the value "hello", we write:
set (MY_VAR "hello")
set (OTHER_VAR "${MY_VAR} world!")
set (SOURCE_FILES foo.cpp bar.cpp baz.cpp)
Project file reloading
It’s important to understand that, when you change the project file (CMakeLists.txt), CLion needs to reload
this file in order to have an updated understanding of the project structure.
The first time you change CMakeLists.txt, you will see a popup where CLion is asking you how you want to reload the file.
The options are:
- To simply reload the file. You will have to keep doing this every time you change the file. Not very convenient!
- Enable auto-reload. This means CLion will monitor changes in CMakeLists.txt and will reload the project on any change.
The option for enabling or disabling auto-reload is also available in CLion’s options under (or for OS X users).
Specifying header search paths
Header search paths are important for both the compiler (so that it knows where to search for headers) but also for CLion,
since the IDE can index the relevant directories and provide code completion and navigation facilities on #include
statements.
The compiler searches the headers in several predefined locations that are specific to the operating system being used.
In addition, you can specify further directories with header files using CMake’s include_directories()
command:
include_directories( ${MY_SOURCE_DIR}/src )
You can control the order of inclusion of the directories with additional BEFORE and AFTER keywords. The default behavior is to append the include directory with the current item:
include_directories( BEFORE ${MY_SOURCE_DIR}/src )
Compiler settings
The compiler settings are used to determine which compiler is being used to compile the code and what options it uses.
CLion also makes use of this information in order to tune its analysis to the features available in the compiler under
the specified settings. CLion uses the GCC or Clang compilers, which you need to download and install before trying to
compile applications in CLion. If you’ve already got a suitable compiler installed, CLion will automatically detect it,
and no further action is required.
If, on the other hand, you want to explicitly specify the compiler to use, you can open up the options under
(or
for OS X users)
and specify the desired compiler by passing in the following string:
-D CMAKE_<LANG>_COMPILER=[fully qualified compiler name]
-D CMAKE_CXX_COMPILER=C:\MinGW\bin\g++
The CMake settings in CLion let you define the options for CMake project generation, such as e.g., defining your own
variables. In addition, you can predefine environment variables:
To find out which environment variables are already defined on the host system, you can use the Show link on
the bottom — this will pop up a separate window:
Setting language standard
To enable a particular language standard, you need to add the respected flag to CMAKE_<LANG>_FLAGS
variable, where
the LANG part specifies the language (C for C and CXX for C++):
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") # enable C99 standard
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") # enable C++11 standard
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
c++1y
, c++1z
and so on.
Changing build directory
An alternative way to pass an argument to CMake, you can use the corresponding CLion settings instead.
CLion provides an input box for specifying the build options, and also lets you change the build output path:
Adding build targets
A build target defines an executable or a library that the CMake script helps you build.
A script can have more than one build target. To create an executable binary, you use the add_executable()
command, i.e.,
add_executable (my_executable ${SOURCE_FILES})
SOURCE_FILES
variable contains all the necessary .cpp
files
required to construct the final executable. If you created a project using CLion, you’ve already got such a variable set up for you,
together with a default build
target.
If you want to build a library instead, you can use the add_library()
command:
add_library (my_library STATIC|SHARED|MODULE ${SOURCE_FILES})
- STATIC builds a static library, i.e. a library that gets embedded into whichever executable decides to use it.
-
SHARED builds a shared library (
.so
on Linux,.dll
on Windows). - MODULE builds a plugin library — something that you do not link against, but can load dynamically at runtime.
Build targets and CLion’s configurations
When you run or debug a target in CLion, you do so based on a configuration. A configuration is a collection of settings that defines what you are building and how. Please note that configurations are not the same as targets— however, it’s worth noting also that CLion automatically creates configurations for each target you have in your project. By default, when you create a new project in CLion, you get two configurations:
- <projectname> is the configuration which matches the name of your both your project and the default target.
- Build All is a configuration which builds all targets.
When it comes to running/debugging, you need to specify the target you actually want to run/debug. You specify the configuration either by going into
or by using CLion’s drop-down menu:
In the configuration editor, you can add and remove new configurations. Removing is important because renaming a target will result in
a stale configuration that will no longer run, and CLion will tell you about it by putting a red cross on top of its icon.
Here’s what the configuration editor looks like. Note that the name of the target and the name of an executable file (in case when the target
is the executable)
are taken directly from CMakeLists.txt from the target's description).
CMakeLists.txt
but also information from the build configuration by setting the so-called resolve context. You can access it by clicking the
Context field of the Status bar:
Including libraries
Here’s how you actually make use of a library. As the first step, you need to instruct the compiler to find a desired library and its components:
find_package (my_library COMPONENTS REQUIRED component1 component2 OPTIONAL_COMPONENTS opt_component)
REQUIRED
determines component1
and component2
as the mandatory components
for a project.
The component opt_component
is qualified as an optional one; this means, that compilation can proceeded regardless of
whether the optional component is actually available.
Next up, you need to link an executable to the located library:
target_link_libraries (my_target ${my_library};${another_library})
target_link_libraries()
shall be placed after add_executable()
command.
Using Boost
The Boost libraries are the most popular C++ library set. While many libraries are header-only (meaning you don’t need to perform any linking, just include a few files), some libraries do, in fact, require compilation and linking.
So let’s get started by including the files. For this, you can use CLion’s incboost live template, which will generate the following:
find_package(Boost)
IF (Boost_FOUND)
include_directories(${Boost_INCLUDE_DIR})
endif()
Now, when it comes to libraries, Boost offers you options for both static and dynamic linking. There are also additional options, such as whether or not you want to enable multithreading (feel free to read up on this option, since it doesn’t actually make code parallel). Also, you can cherry-pick which Boost components you want:
set (Boost_USE_STATIC_LIBS OFF) # enable dynamic linking
set (Boost_USE_MULTITHREAD ON) # enable multithreading
find_package (Boost COMPONENTS REQUIRED chrono filesystem)
target_link_libraries()
command:
target_link_libraries (my_target ${Boost_LIBRARIES})
An alternative approach to using libraries is to use an external dependencies manager that can use CMake to configure and build projects.
Including sub-projects
Projects can have dependencies to other projects. CMake does not have the notion of a ‘solution’ — instead, it allows you to define dependencies between projects. Typically, you want to organize your multi-project workspace such that:
- When opening the main project, the IDE shall open all the dependant projects as well.
- All the settings of the main project shall be applied to the dependant projects automatically.
- All the smart features (e.g refactoring, code completion, etc) shall take effect in all the projects.
The above points can be achieved with proper configuration of the CMakeLists file. Essentially, you need to organize your projects
as a tree of subdirectories with the top-level CMakeLists.txt
file in the root directory.
Then, each subdirectory of that tree shall represent a single sub-project and contain its own CMakeLists.txt
file,
which is then included to the main project in the top-level CMakeLists.txt
file, as follows:
add_subdirectory (project1) # including project1 into the main project
add_subdirectory (project2) # including project2 into the main project
CMake Cache
When you run CMake for the first time, what it will do is assemble the so-called CMake cache, collecting the variables
and settings found in the system and storing them so it doesn’t have to regenerate them later on.
In most cases, you should not worry about this cache, but if you want to fine-tune it, you can. CLion gives you an editor
window where you can view and edit the values:
If you’ve changed values, be sure to press the Apply Changes and Reload button so that your changes are actually internalized by CLion.