CMake Tutorial – Chapter 3: GUI Tool

Introduction

Although when we looked at IDE projects generated by CMake we still used the command line. You can also use the CMake GUI to generate and configure projects. This can be convenient if you don’t like the command line, however it can be even more useful than that.

CMake stores a lot of configuration settings in the project’s cache. This cache can be viewed and edited using the CMake GUI. This can be quite useful for seeing how a project is configured as the settings are presented in a nice list. You can also change these values so you can set your build type to “Release” to make a release build or you can add specific compiler flags.

First Fix a Warning

In chapter 2 when covering the Xcode generator I said that I’d fix the warning we saw later. Well it looks like later has come. The first thing we need to do is give the compiler some more flags so that we can reproduce the warning.

CMakeLists.txt

New or modified lines in bold.
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
set(CMAKE_LEGACY_CYGWIN_WIN32 0)

project("To Do List")

enable_testing()


if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
    "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
    set(warnings "-Wall -Wextra -Werror")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
    set(warnings "/W4 /WX /EHsc")
endif()
set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS} ${warnings}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${warnings}")

add_executable(toDo main.cc
                    ToDo.cc)

add_test(toDoTest toDo)
if(...), elseif(...), else(), endif()
While everything in CMake looks like a function call control flow is available. Its if syntax is rather strange so be sure to keep the documentation handy. The arguments passed to else() and endif() are ignored, but they can be useful for documentation purposes.
CMAKE_<LANG>_COMPILER_ID
These variables identify the type of compiler being used. Here we are using it to be able to pass different flags to different compilers as needed. Since Clang accepts the same arguments as GCC I grouped them together. A list of possible values is provided by the documentation . Obviously my if statement is not exhaustive as it only covers the 3 compilers I have readily available.
set(variableName value…)
Set a variable with the given name to a particular value or list of values. (Lists will be covered later)
set() documentation
CMAKE_<LANG>_FLAGS
These variables store the flags that will be passed to the compiler for all build types. In this particular case we wanted to add some flags that control warnings. (Build types will be covered later in this chapter.)
Note: This variable is a string containing all of the flags separated by spaces; it is not a list.
In this case we are turning on most warnings and having the compiler treat them as errors. (This is, in fact, Microsoft’s suggestion for all new projects.) Since we only want to add these options we append them to the end of the existing flags string.
CMake does offer some string functions, but not for something as simple as appending to an existing string.
A few notes about MSVC: The /EHsc flag enables complete C++ exception handling which is required by iostream. (/EH documentation ) More importantly is that CMake will convert Unix-style flags to Microsoft-style flags automatically for you. So we could have used "-W4 -WX -EHsc" instead and it would have worked. This means that any common flags do not need to be defined separately for MSVC. I would, however, recommend always using Microsoft-style flags for MSVC specific flags. Then not only is it obvious that they are MSVC flags, but they are also easier to look up since you won’t have to remember to translate them yourself.

Now if we build not only should we see more warnings and since they are being treated as errors they should also prevent the build from completing. Since warnings usually point to potential problems I always set up my CMakeLists.txt to enable stricter warnings and treat them as errors. Developing this way can be a bit annoying, but in the long run it will lead to cleaner code and, in theory, fewer defects.

 > mkdir build
 > cd build
 > cmake -G "Unix Makefiles" ..
-- The C compiler identification is Clang 4.1.0
-- The CXX compiler identification is Clang 4.1.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Volumes/Documents/Programming/C++/CMake Tutorial/flavors/mac/part3/build
 > make
Scanning dependencies of target toDo
[ 50%] Building CXX object CMakeFiles/toDo.dir/main.cc.o
/Volumes/Documents/Programming/C++/CMake Tutorial/flavors/mac/part3/main.cc:22:12: error: 
      unused parameter 'argc' [-Werror,-Wunused-parameter]
    int    argc,
           ^
/Volumes/Documents/Programming/C++/CMake Tutorial/flavors/mac/part3/main.cc:23:12: error: 
      unused parameter 'argv' [-Werror,-Wunused-parameter]
    char** argv
           ^
/Volumes/Documents/Programming/C++/CMake Tutorial/flavors/mac/part3/main.cc:58:19: error: 
      comparison of integers of different signs: 'const unsigned long' and
      'const int' [-Werror,-Wsign-compare]
    if (testValue != expectedValue)
        ~~~~~~~~~ ^  ~~~~~~~~~~~~~
/Volumes/Documents/Programming/C++/CMake Tutorial/flavors/mac/part3/main.cc:34:15: note: 
      in instantiation of function template specialization
      'equalityTest<unsigned long, int>' requested here
    result |= EXPECT_EQUAL(list.size(), 3);
              ^
/Volumes/Documents/Programming/C++/CMake Tutorial/flavors/mac/part3/main.cc:8:36: note: 
      expanded from macro 'EXPECT_EQUAL'
#define EXPECT_EQUAL(test, expect) equalityTest( test,  expect, \
                                   ^
3 errors generated.
make[2]: *** [CMakeFiles/toDo.dir/main.cc.o] Error 1
make[1]: *** [CMakeFiles/toDo.dir/all] Error 2
make: *** [all] Error 2

This time CMake found Clang and with our new flags we have 3 errors. (Rather nice errors, actually.) These errors are actually simple to fix, so lets fix them before we move on.

main.cc

New or modified lines in bold.
#include <iostream>
  using std::cerr;
  using std::cout;
  using std::endl;

#include "ToDo.h"

#define EXPECT_EQUAL(test, expect) equalityTest( test,  expect, \
                                                #test, #expect, \
                                                __FILE__, __LINE__)

template < typename T1, typename T2 >
int equalityTest(const T1    testValue,
                 const T2    expectedValue,
                 const char* testName,
                 const char* expectedName,
                 const char* fileName,
                 const int   lineNumber);


int main(
    int,
    char**
)
{
    int result = 0;

    ToDo list;

    list.addTask("write code");
    list.addTask("compile");
    list.addTask("test");

    result |= EXPECT_EQUAL(list.size(),     size_t(3));
    result |= EXPECT_EQUAL(list.getTask(0), "write code");
    result |= EXPECT_EQUAL(list.getTask(1), "compile");
    result |= EXPECT_EQUAL(list.getTask(2), "test");

    if (result == 0)
    {
        cout << "Test passed" << endl;
    }

    return result;
}


template < typename T1, typename T2 >
int equalityTest(
    const T1    testValue,
    const T2    expectedValue,
    const char* testName,
    const char* expectedName,
    const char* fileName,
    const int   lineNumber
)
{
    if (testValue != expectedValue)
    {
        cerr << fileName << ":" << lineNumber << ": "
             << "Expected " << testName << " "
             << "to equal " << expectedName << " (" << expectedValue << ") "
             << "but it was (" << testValue << ")" << endl;

        return 1;
    }
    else
    {
        return 0;
    }
}

[zip file] Source

They were rather simple errors to fix. The simplest solution to unused function parameters is to delete their names leaving only the types, if it’s temporary just comment them out. This documents both for other people and the compiler that the parameters aren’t being used. The last error is caused by literal numbers defaulting to being ints. If we construct a size_t the problem is fixed.

CMake GUI

Generating Our Project

The CMake GUI allows one to easily run CMake without having to use the command line. It also makes it easier to set or change specific options, which we will explore.

[blank CMake window]

The first two entries should be familiar, but more explicit than what we saw earlier. To relate to the command line we were using: cd <Where to build the binaries>; cmake <Where is the source code>. That command line also configures and generates, which you would do using the “Configure” and “Generate” buttons, of course. The bulk of the window is for variables, which are only visible once you have configured.

It isn’t quite that simple, though. Once you pick your source and build directories and then click “Configure” CMake will ask you about which generator you want to use and more.

[generator window]

The displayed options are the typical ones used so far during this tutorial. Generate Unix Makefiles and use the default native compilers. A different generator can be chosen from the list rather than having to carefully type it, which can be handy. The other options allow you to specify which compiler to use, a topic that will be covered later. Clicking “Finish” will then actually configure.

Note: This step can only be done the first time, so if you want to use a different generator (or compiler) you will have to start over with an empty build directory.

[CMake window after configuring]

Notice that the bottom section displays the same output the cmake command displays when configuring. There are also now some variables displayed in the central portion of the window. In this example most are specific to Mac OS X. The variables’ values can easily be changed by double clicking in the “Value” field and entering a new value.

CMAKE_BUILD_TYPE
This variable controls the type of build to be done. The possible values are empty, Debug, Release, RelWithDebInfo, and MinSizeRel. The values’ meanings are relatively obvious. Based upon the value of this variable CMake will set the compiler flags appropriately. This is done by adding the value of the variable CMAKE_<LANG>_FLAGS_<BUILD_TYPE> to CMAKE_<LANG>_FLAGS. By setting these variables appropriately you can control the compiler flags for the various types of builds.
Note: This variable is not available with all generators. Some IDE generators create non-Makefile projects, e.g. Visual Studio, in which case the build type is handled by the IDE itself.
CMAKE_BUILD_TYPE Documentation
CMAKE_INSTALL_PREFIX
CMake can create an install target which will be covered in a future chapter. This prefix can be set to control where things are installed. It is similar to the --prefix argument for configure scripts.
However if you are curious: CMAKE_INSTALL_PREFIX Documentation

Simply click “Configure” again as directed. Clicking “Generate” will then generate our Makefile so we can build.

CMake Cache

If you check the “Advanced” box all cache variables will be listed.

[advanced variables]

CMake stores variables that need to be persistent in the cache. These include things such as the path to your compiler and the flags for the compiler. Naturally one should be careful when editing variables in the cache.

You will notice that the compiler flags we added earlier do not appear in the cache. While this might be a good idea as it forces those options to always be used it really isn’t correct. We can tell set() to put the variable in the cache, however it’s not that simple. Either the cache will never be updated or our options will be appended every time CMake configures.

The following should do the trick:

CMakeLists.txt

New or modified lines in bold.
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
set(CMAKE_LEGACY_CYGWIN_WIN32 0)

project("To Do List")

enable_testing()


if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
    "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
    set(warnings "-Wall -Wextra -Werror")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
    set(warnings "/W4 /WX /EHsc")
endif()
if (NOT CONFIGURED_ONCE)
    set(CMAKE_CXX_FLAGS "${warnings}"
        CACHE STRING "Flags used by the compiler during all build types." FORCE)
    set(CMAKE_C_FLAGS   "${warnings}"
        CACHE STRING "Flags used by the compiler during all build types." FORCE)
endif()


add_executable(toDo main.cc
                    ToDo.cc)

add_test(toDoTest toDo)


set(CONFIGURED_ONCE TRUE CACHE INTERNAL
    "A flag showing that CMake has configured at least once.")

[zip file] Source

if (NOT CONFIGURED_ONCE)
In CMake an undefined variable evaluates to false. Because of this we can use CONFIGURED_ONCE as a flag to determine if CMake has configured this project at least once.
Defined variables that are empty or contain 0, N, NO, OFF, FALSE, NOTFOUND or variable-NOTFOUND are also considered false.
set(CMAKE_CXX_FLAGS “${warnings}” CACHE STRING “Flags used by the compiler during all build types.” FORCE)
Initialize the value of CMAKE_CXX_FLAGS to be the desired warning flags. The syntax for this form of the set command is explained below. Two things to note:
  1. The docstring is exactly what CMake uses by default. When overriding built-in CMake variables be sure to use the same docstring as it does to avoid confusion.
  2. We need to force this value to be stored in the cache because the built-in variables are present in the cache even before the first time our project is configured. This is why we need the CONFIGURED_ONCE variable.
set(CONFIGURED_ONCE TRUE CACHE INTERNAL “A flag showing that CMake has configured at least once.”)
Set CONFIGURED_ONCE to true and store it in the cache since by now configuration is complete. We don’t need to force this as CONFIGURED_ONCE is not present in the cache.

A new form of the set command was used this time to store variables in the CMake project’s cache. It is explained here and also in CMake’s documentation

set(variableName value … CACHE type docstring FORCE)
This form of the set function allows you to store a variable in CMake’s cache. The cache is both global and persistent. For both of these reasons it can be quite useful and should be used carefully. The other important thing about the cache is that users can, for the most part, edit it. The CACHE flag is a literal that tells CMake you want to store this variable in the cache.
type
The type of value being stored in the cache. Possible values:
FILEPATH
A path to a file. In the CMake GUI a file chooser dialog may be used.
PATH
A path to a directory. In the CMake GUI a directory chooser dialog may be used.
STRING
An arbitrary string.
BOOL
A boolean on/off value. In the CMake GUI a checkbox will be used.
INTERNAL
A value of any type with no GUI entry. This is useful for persistent, global variables.
docstring
A string that describes the purpose of the variable. If only specific values are allowed list them here as the user will see this string in the CMake GUI as a tool tip.
FORCE (optional)
Force this entry to be set in the cache. Normally if a variable already exists in the cache future attempts to set it will be ignored unless FORCE is the last argument. Please note that setting a variable in the cache is dependent on the variable already being in the cache not on its emptiness. Because of this and the fact that many of the CMake variables exist in the cache before your CMakeLists.txt is processed you need to test for the first configuration as done above.

CMake Curses Interface

Introducing ccmake

CMake also includes a command line curses-based interface, ccmake. It provides equivalent functionality to that of the GUI. Most installations include this tool, although not all. The ccmake tool can be used both to create a CMake build or edit the cache of one. To create a new build it is used very similarly to cmake:
ccmake path-to-source
Naturally editing a build’s cache is quite similar:
ccmake path-to-existing-build
For the most part this tool is very much like the GUI except, of course, its interactions are all keyboard based. It can be useful if you often connect to your build machine via an ssh session or you don’t want the dependency of Qt, which the GUI requires.

[ccmake curses interface]

The main difference between this tool and the GUI is that it won’t walk you through setting up a build, you have to provide paths on the command line. Besides that its features are mostly the same. Of course, instead of clicking the “Configure” and “Generate” buttons you would use the c and g keys.

Useful Makefile Targets

There are two built-in make targets that CMake provides that are useful for managing the cache. These are especially useful if you work from the command line a lot.

make rebuild_cache
This target re-runs CMake for your build having the same effect as cmake ., this can be handy, though, if you have multiple versions of CMake installed or don’t have cmake in your path as this target knows the path to the cmake that was originally used to generate the build.
make edit_cache
Very similar to the above target except this one runs the appropriate ccmake, or cmake-gui if ccmake isn’t installed. The reasons for this being useful are the same, too.

Most of the time these targets aren’t used, but as they can be handy it’s good to know about them.

There is one last Makefile target that is useful, especially on larger projects: make help. This prints a list of targets provided by the Makefile. This can be convenient if you only want to build specific targets but aren’t sure how they were named.

Revision History
VersionDateComment
12013-03-28Original version.
22013-04-14Updated MSVC compiler flags and added note about automatic flag-style conversion.
32013-07-14Added line numbers and indication of changes to code sample. Added a link to the section on lists.

10 thoughts on “CMake Tutorial – Chapter 3: GUI Tool

  1. Sorry for my bad English.

    Can you explain more about “CMake Cache” section, I don’t really understand:
    – Why “the compiler flags we added earlier do not appear in the cache” ?
    – We tell CMake add them to the cache what for ?

    • The CMake cache does two things:
      1. It stores some variables persistently between individual runs of CMake.
      2. You can edit most variables in the cache using the CMake GUI or the curses interface ccmake.

      In many ways 2 is the most important. Using the GUI one can explore what parameters are available for the build, this can make certain options “discoverable”. For example you can use the GUI to change the build type (CMAKE_BUILD_TYPE) even if you don’t remember the exact name of the option or the available values since a list of variables in the cache is displayed and its help string lists the valid values. The one I find most useful is being able to quickly change the compiler flags, this is especially helpful if ever you need to write a preprocessor macro. If using Clang or GCC you can easily add, and later remove, the -save-temps flag using the CMake GUI.

      The storage of variables provided by the cache is useful for a variety of things. As mentioned above persisting the chosen build type and compiler flags. Also things that are expensive to calculate can be saved so that they don’t have to be calculated every time CMake is run. This last part is very useful when using the find_package command.

      To, finally, answer your questions:
      – When we used set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${warnings}") we didn’t specify that the new value for the variable CMAKE_CXX_FLAGS should be stored in the cache so the cache was not updated. Based on the way storing variables in the cache works I would guess that CMake only intends for the cache to be set programmatically the firs time it is run and then only updated by the user after that. I am assuming this because you have to use the FORCE option to modify a variable that is already in the cache.
      – Updating the compiler flags in the cache is done for two reasons. First this way you can easily see what the compiler flags are by looking at the cache rather than by inspecting the actual compile command (although doing so is not a bad idea). The second is that it allows you to edit the flags using the CMake GUI. If we had not updated the cache we would be simply adding the flags on every time CMake is run and the only way you could remove one of those flags would be by editing the CMakeLists.txt file. As to which way is better probably depends upon the situation.

  2. Dear John,

    Seems to me that some of the pictures are no longer valid. Could you kindly check them? Thanks!

    For example:

    For the most part this tool is very much like the GUI except, of course, its interactions are all keyboard based. It can be useful if you often connect to your build machine via an ssh session or you don’t want the dependency of Qt, which the GUI requires.

    [ccmake curses interface]
    The main difference between this tool and the GUI is that it won’t walk you through setting up a build, you have to provide paths on the command line. Besides that its features are mostly the same. Of course, instead of clicking the “Configure” and “Generate” buttons you would use the c and g keys

  3. // Comment: This tutorial is now my favorite site for learning CMake – thanks!

    I’m not sure if it was your intent, but you get different final values for CMAKE_CXX_FLAGS when you compare the CMakeLists.txt at the top of this page and the one at the bottom.

    TOP:
    set(CMAKE_CXX_FLAGS “${CMAKE_CXX_FLAGS} ${warnings}”)

    BOTTOM:
    set(CMAKE_CXX_FLAGS “${warnings}”

    In the 1st case you are appending your compile flags, but in the 2nd case you are overwriting anything that was there before with your new flags.

    • The main diffference between the two is that the second time the value for CMAKE_CXX_FLAGS is set in the cache the first time the build is configured. Subsequent configure steps for that build leave the value alone. So the flags are initialized as desired and then using cmake-gui or ccmake to adjust the flags. Now that I think about it perhaps the two should be the same so you could augment the flags the first time configuring a build.

  4. Does it go against CMake’s philosophy to have compiler flags set in the CMakeLists.txt file? I’ve seen arguments that those should exist in a separate toolchain file (e.g. VS2015Toolchain.cmake).

    • That is an excellent question. This is an example of something that I’m not sure what the CMake philosophy is. In some ways the compiler flags are project specific yet they are also compiler specific. Perhaps the answer is that it depends on the flags and the project. One program I’ve worked on has a separate CMake file that has all the compiler flags and definitions in it which appends the projects chosen flags (which are for GCC, Clang, and Visual Studio) to CMAKE_CXX_FLAGS every time CMake runs. In that situation we, the developers, have decided which flags should be used and force them while allowing individual developers to add more beforehand if desired.

      As I don’t know of any common CMake philosophy I would say that it depends on the philosophy of the project and the importance and type of compiler flag. (I’m not sure this answer is of any help…)

  5. i just want to know when i press “configure” with some options enabled manually, how i can see the full command line used by cmake-gui for generate configuration ?

    • I wonder why I’ve never asked this question myself. As it turns out there’s a good answer to your question. At least in the most recent version of cmake-gui you can get close. If you choose the “Tools” > “Show My Changes” menu item. It will show the command line options for the variables you defined. It won’t include the generator you chose, but that’s probably less important.

Leave a Reply

Your email address will not be published. Required fields are marked *