CMake Essentials Series - Part #1

Written By Tom Hulton-Harrop

Topic

This series aims to give an overview of some of the most useful functionality in CMake and how to apply it.

Motivation

CMake is the most widely used build system in the C++ community and understanding how best to use it unlocks a whole host of possibilities when it comes to C++ development. Project setup becomes a breeze and integration with open source projects is made much simpler.

Example

To begin this series we’ll show the minimum amount of code required to get a 'Hello, World!' application up and running. First create a new folder and in it create an empty main.cpp and CMakeLists.txt file.

> mkdir cmake-essentials-part1 && cd cmake-essentials-part1
> touch main.cpp && touch CMakeLists.txt`

The CMakeLists.txt file looks like this:

cmake_minimum_required(VERSION 3.15) #1
project(cmake-essentials-part1 LANGUAGES CXX) #2
add_executable(${PROJECT_NAME}) #3
target_sources(${PROJECT_NAME} PRIVATE main.cpp) #4
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) #5

First we set the CMake version (#1). This line always has to come first in a top-level CMakeLists.txt file (3.15 is a safe bet but feel free to pick a newer version). Next is the project command (#2), this should immediately follow cmake_minimum_required (LANGUAGES is optional but good practice). We then create the target to build (#3), in this case an executable (${PROJECT_NAME} maps to cmake-essentials-part1 , the name set in the project command).

Following that we set the sources to be built (#4). As with all* target_ commands we must specify the scope of the items - here we use PRIVATE as these files are not going to be relied on by some downstream dependency. Finally we set the C++ version (#5), which again is not strictly required but good practice (note we’re not touching compiler flags here, CMake will handle this for us).

*technically not all, but it’s now best practice to always provide this even if legacy commands do work without it.

main.cpp should look familiar:

#include <iostream>

int main(int argc, char argv[]) {
    std::cout << "Hello, World!\n";
    return 0;
}

With that setup, the last thing to do is to run CMake.

> cmake -S . -B build # 1
> cmake --build build # 2

The first line (#1) will run the CMake configure step to generate host build files (on Windows this will likely default to Visual Studio and can be specified with -G ). The following line will invoke whatever build system the configure step generated and leave your application in the build folder (with Visual Studio this will be build/Debug/cmake-essentials-part1.exe ).

Deliberation

This might seem like mildly more work than opening Visual Studio and creating a project from there, but the massive advantage to this approach is you’ve now created a completely portable C++ program you can build from source on any operating system that supports CMake (most do!). You can easily share this with someone else to quickly build a demo application and even better, with only a few more commands it’s possible to start integrating third-party libraries (no more searching through include paths and linker settings in Visual Studio configuration windows).

Further Reading

One of the most widely recommended talks on CMake is an excellent presentation by Daniel Pfeifer titled ‘Effective CMake’

It covers a lot of ground and gives a thorough introduction to the current CMake landscape and best practices.

To be continued

In the next entry in this series we’ll look at bringing in some third-party dependencies to our project.

Author: Tom Hulton-Harrop

Disclaimer: The views expressed here are those of the individual author and do not represent those of Lumberyard or Amazon.

4 Likes

I do have one main question about cmake will it be compatible with projects made with waf or will those project need to be redone for cmake?

Sorry to come back to this, but is it know just yet if projects will smoothly transition from WAF to CMAKE? @lmbr_Saulty Thanks for any response!

Hi @WashedUpStudios, no I’m afraid they are different builds and we currently don’t have plans to make it backwards compatible. But I have asked the dev team to see if there might be work arounds and will post it here if I hear back.

Thanks for the update no problem if its not, I was just wondering to see if I want to start turning more things into slices now or not for later releases. This reply is a huge time saver thank you and the team a lot!

1 Like