CLion 2022.1 Help

Quick CMake tutorial

This tutorial will guide you through the process of creating and developing a simple CMake project. Step by step, we will learn the basics of CMake as a build system, along with the CLion settings and actions for CMake projects.

1. Basic CMake project

CMake is a meta build system that uses scripts called CMakeLists to generate build files for a specific environment (for example, makefiles on Unix machines). When you create a new CMake project in CLion, a CMakeLists.txt file is automatically generated under the project root.

Let’s start with creating a new CMake project. For this, go to File | New Project and choose C++ Executable. In our example, the project name is cmake_testapp and the selected language standard in C++14.

By default, we get the project with a single source file main.cpp and the automatically created root CMakeLists.txt containing the following commands:

new cmake project

Command

Description

cmake_minimum_required(VERSION 3.13)

Specifies the minimum required version of CMake. It is set to the version of CMake bundled in CLion (always one of the newest versions available).

project(cmake_testapp)

Defines the project name according to what we provided during project creation.

set(CMAKE_CXX_STANDARD 14)

Sets the CMAKE_CXX_STANDARD variable to the value of 14, as we selected when creating the project.

add_executable(cmake_testapp main.cpp)

Adds the cmake_testapp executable target which will be built from main.cpp.

2. Build targets and Run/Debug configurations

Target is an executable or a library to be built using a CMake script. You can define multiple build targets in a single script.

For now, our test project has only one build target, cmake_testapp. Upon the first project loading, CLion automatically adds a Run/Debug configuration associated with this target:

default configuration for a new cmake project

Click Edit Configurations in the switcher or select Run | Edit Configurations from the main menu to view the details. The target name and the executable name were taken directly from the CMakeLists.txt:

details of the default configuration for a new cmake project

Notice the Before launch area of this dialog: Build is set as a before launch step by default. So we can use this configuration not only to debug or run our target but also to perform the build. To learn more about various build actions available in CLion, see Build actions.

3. Adding targets and reloading the project

Now let’s add another source file calc.cpp and create a new executable target from it.

Right-click the root folder in the Project tree and select New | C/C++ Source File. CLion prompts to add the file to an existing target:

add a new source file

Since our goal is to create a new target, we clear the Add to targets checkbox. Accordingly, CLion notifies us that the new file currently does not belong to any target:

file not belonging to the project

Now let's declare a new target manually in the CMakeLists.txt. Note that CLion treats CMake scripts as regular code files, so we can use code assistance features like syntax highlighting, auto-completion, and navigation:

completion in cmake script
When we make changes in CMakeLists.txt, CLion needs to reload it in order to update the project structure:
reload cmake project after adding a new target

We can either reload the project once (Reload changes) or enable automatic reload to let CLion silently apply all the changes in CMakeLists.txt. The option for enabling/disabling auto-reload is also available in Settings / Preferences | Build, Execution, Deployment | CMake.

After reloading the project, CLion adds a Run/Debug configuration for the new target:

configuration for the newly added target

Library targets

Up to this point, the targets we added were executables, and we used add_executable to declare them. For library targets, we need another command - add_library. As an example, let's create a static library from the calc.cpp source file:

add_library(test_library STATIC calc.cpp)

As well as for executables, CLion adds a Run/Debug configuration for the library target after reloading the project:

configuration for the newly added library target

However, this is a non-executable configuration, so if we attempt to run or debug it, we will get the App general balloon error Executable not specified error message.

To obtain the library file, we need to build the test_library target. For this, we can switch to the corresponding configuration and press App actions compile, or call Build | Build "test_library". The libtest_library.a file will appear in the cmake-build-debug folder.

4. Build types and CMake profiles

All the Run/Debug configurations created by far were Debug configurations, which is the default build type of the CMake profile that was automatically configured for our project. CMake profile is a set of options for the project build. It specifies the toolchain, build type, CMake flags, path for storing build artifacts, make build options, and environment variables.

For example, to separate the Debug and Release builds, we can add App general inline add hover a new CMake profile in Settings / Preferences | Build, Execution, Deployment | CMake and set its build type to Release:

Adding a CMake profile

Notice the Build directory field that specifies the location of build results. The default folders are cmake-build-debug for Debug profiles and cmake-build-release for Release profiles. You can always set App actions menu open other locations of your choice.

Now the Run/Debug configuration switcher shows two available profiles:

cmake profiles in the configuration switcher

Switching configurations or CMake profiles may affect preprocessor definitions used while resolving the code. For example, when there are separate flags for Debug and Release builds, or when there are some variables that take different values depending on the build type. This is called resolve context.

Resolve context defines how CLion performs syntax highlighting and other code insights like Find Usages, refactorings, and code completion. When you switch between configurations, the resolve context for the current file is changed automatically. Also, you can select it manually in the context switcher (<Select automatically> restores the automatic selection):

Resolve context switcher

5. Adding include directories

In order to use additional headers located in separate directories, we need to add them either to all the targets or to some specific ones.

For example, let’s create three directories under the project root, includes, includes/general, includes/math, and write the following commands in CMakeLists.txt:

include_directories(includes/math) include_directories(includes/general)

These two commands make the headers located in general and math available for including from the sources of all targets. For example, we can write #include "header_math.h" in calc.cpp.

Static libraries

On step 3, we created a static library called test_library (the default filename is libtest_library.a).

Let's create a lib directory under the project root and copy libtest_library.a from its default location (cmake-build-debug) to this folder.

We will use two commands to link our static library to the cmake_testapp target: find_library provides the full path, which we then pass directly into the target_link_libraries command via the ${TEST_LIBRARY} variable:

linking a static library

Note: make sure to place target_link_libraries after the add_executable command, so that CMake actually builds the target before linking the library.

Dynamic libraries (Boost example)

To illustrate linking dynamic libraries, we will take an example of using Boost.Test framework.

Let's write a simple function int add_values (int a, int b) { return a+b;} in calc.cpp and create the associated header calc.h with the function declaration. We will test this function with the help of Boost.Test framework.

As our project gets more complicated, the root CMakeLists.txt file can become difficult to maintain. To avoid this, and to build a transparent project structure, we will extract the tests into a subproject.

  1. In the Project tree, right-click on the root directory and select New | Directory.

  2. Call the new directory test. It will appear in the tree under the project root.

  3. Right-click the test directory and select New | C/C++ Source file.

  4. Call the file tests.cpp.

  5. Right-click the test directory once again and select New | CMakeLists.txt.

  6. As a result, the project structure will be updated to the following:

    subproject for tests

The subdirectory test/CMakeLists.txt script is initially empty. We can start filling it up by inserting a live template for Boost with libs. Press Ctrl+J or click Code | Insert Live Template, and choose boost_with_libs:

boost live template

Adjust the inserted code to the following:

set(Boost_USE_STATIC_LIBS OFF) #enable dynamic linking # search for unit_test_framework find_package(Boost REQUIRED COMPONENTS unit_test_framework) include_directories(${Boost_INCLUDE_DIR}) # create a cmake_testapp_boost target from test.cpp add_executable(cmake_testapp_boost tests.cpp) # link Boost libraries to the new target target_link_libraries(cmake_testapp_boost ${Boost_LIBRARIES}) # link Boost with code library target_link_libraries(cmake_testapp_boost test_library)

After reloading the changes in both CMakeLists.txt files, CLion creates a Run/Debug configuration for the cmake_testapp_boost target. This is a regular CMake Application configuration which we could run/debug right away. However, to be able to use the built-in test runner, let's create another configuration out of the Boost.Test template:

boost test run/debug configuration

Now we can run this configuration and get test results. Test runner shows the tree of tests in the suite, their output, status, and duration:

boost test runner

7. Working with CTest

This chapter gives a simple example of how to use CTest, a framework for compiling and running tests as part of the CMake build process. Find general description of the framework in CTest support.

Add CTest to the sample project

  1. Create a subdirectory inside test and call it ctest.

  2. Add two source files, addvalues_zero.cpp and addvalues_negpos.cpp, a header file (where we will place the assertion macro) assert_macro.h, and a CMakeLists.txt script:

    Adding a folder for CTest
  3. Add the following lines to ctest/CMakeLists.txt:

    cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR) add_executable(ctest_exe_addvalues_zero addvalues_zero.cpp) add_executable(ctest_example_addvalues_negpos addvalues_negpos.cpp) add_test(ctest_addvalues_zero ctest_exe_addvalues_zero) add_test(ctest_addvalues_negpos ctest_example_addvalues_negpos)

    The first line states the minimum supported version of CTest, which corresponds to the version of CMake, 3.14.

    We are using the add_test command here to register our executables, ctest_exe_addvalues_zero and ctest_example_addvalues_negpos, with CTest.

  4. Now we can place the actual code inside the test sources:

    • assert_macro.h:

      #include <iostream> #include <sstream> #define assertEqual( ... ) \ do { \ if( !( __VA_ARGS__ ) ) { \ std::cerr << "Unit test assert [ " \ << ( #__VA_ARGS__ ) \ << " ] failed in line [ " \ << __LINE__ \ << " ] file [ " \ << __FILE__ << " ]" \ << std::endl; \ err_code = 1; \ } \ } while( false )

    • addvalues_zero.cpp:

      #include "assert_macro.h" int test_addvalues_zero() { int err_code = 0; assertEqual (0+0 == 0); assertEqual ((-5)+5 == 0); return err_code; } int main() { return test_addvalues_zero(); }
    • addvalues_negpos.cpp:

      #include "assert_macro.h" int test_addvalues_negpos() { int err_code = 0; assertEqual ((-5)+10 == 5); assertEqual ((-10)+5 == -5); assertEqual (5+(10) == 5); //test to fail return err_code; } int main() { return test_addvalues_negpos(); }
  5. Next, we need to enable CTest and declare the subproject in the top-level CMakeLists.txt:

    enable_testing() add_subdirectory(test/ctest)

    The enable_testing command creates a built-in target test which will execute CTest.

  6. Reload the project.

After the reload, CLion detects the tests and creates a ready-to-go All CTest configuration:

All CTest configuration in the switcher

If we run this configuration, the results will be shown in the Test Runner window, similarly to other supported testing frameworks:

CTest results

We can also use the gutter menu next to the add_test commands in ctest/CMakeLists.txt to run or debug our tests:

Gutter menu to ctests

In case of running/debugging a test via the gutter menu, CLion creates a temporary configuration, so if we go to Edit Configuration, we will find two automatically generated configurations of the CTest Application type:

Automatically created CTest configurations

Let's check the All CTest configuration. Click the pen icon next to the Test list to run field. This opens the List of Available Tests dialog, where we can change the set of tests:

List of the available tests in CTest configuration

8. Learn more

To dig deeper into CMake in CLion, learn how to:

Last modified: 25 July 2022