Last week, we’ve started our little CMake project with a “Hello CMake” one-liner. Now it’s time to expand that example by adding another target and information about the project.

This post is the second of a series about CMake:

Adding more files to the project

Simply saying “Hello CMake” is, of course, not enough for our high standards. We want to say hello to anyone, so we add header and source files for a more general hello function:

void hello(std::string const& name) { std::cout << "Hello, " << name << "!

"; }

Adding the additional header and source to our CMakeLists.txt is pretty straightforward:

add_executable(prog main.cpp hello.cpp hello.h)

As you see, the add_executable command can take the whole list of files we have. See the full code on GitHub.

Why did I just add the headers?

The headers are not compiled themselves, and the compiler will find them whenever they are included. So, for a Makefile-based project, it does not make much sense to explicitly add them. However, when we generate other things, e.g. MSVC project files, we want to have the headers in those generated projects.

Therefore, we add the headers to the list of sources for our executable. The different CMake generators do the right thing: add them to IDE project files, but ignore them when it comes to actually building the project.

Add another target

You may have seen the last line of output when CMake builds our program and wondered about the meaning: [100%] Built target prog

In CMake’s terminology, targets are the things we build. In most cases, those are executables and libraries. We will now add another target to our project.

Since we are exemplary developers, we add unit tests to our project as soon as possible. Today, I’ll use Catch 2. For now, we throw everything into our main directory. We’ll restructure and clean up soon.

Adding the second target is pretty easy in CMake:

add_executable(prog main.cpp hello.cpp hello.h) add_executable(tests testmain.cpp hello.cpp hello.h)

I have omitted catch.hpp in the tests because it does not really belong to the test project. Find the current state of our project on GitHub – but promise to not look too closely at the test case. Redirecting std::cout to test the function is ugly, especially since we are not using C++11 yet.

Variables

Looking ahead, we will probably be testing every source file of our main project except main.cpp itself. That makes a lot of duplications if we continue to list them in both executables. As any proper language, CMake has variables to solve this:

set(PROJECT_SOURCES hello.cpp hello.h) add_executable(prog main.cpp ${PROJECT_SOURCES}) add_executable(tests testmain.cpp ${PROJECT_SOURCES})

The set command in CMake is used to define variables. The first word is the name of the variable, and the rest becomes its content. Unless it contains the words CACHE and/or PARENT_SCOPE which I’ll not cover here.

CMake variables can be lists and can be seamlessly used one after another to make longer lists. For example, if we later choose to list all test sources in a variable, the target definition could look like add_executable(tests ${TEST_SOURCES} ${PROJECT_SOURCES}) .

Some housekeeping

We’ve jumped right into our CMake project without looking at the standard stuff every project should have. So let’s take a step back and quickly talk about the things that make our project a little more usable.

The CMake syntax allows comments. Like so many languages, a comment starts with a # and goes until the end of the line – like // in C++.

The minimum CMake version

CMake has evolved over the years and features have been added. New versions appear relatively quickly, at the time of this writing, the latest stable version is 3.11. If you use newer features, users with older CMake versions might get strange error messages, or worse, things might get parsed but not built correctly.

To avoid that, CMake has the cmake_minimum_required command which takes the minimum version number and should be the first thing in our project’s CMakeLists.txt. cmake_minimum_required(VERSION 3.6)

A good first candidate for the minimum version is the version of CMake you are using right now. You get it by running cmake --version .

The project

Let’s for a second use one of the Visual Studio generators instead of the Makefile generator, in a separate directory _msvc_build. We’ll see that, besides some project files for our targets, we also get a solution file named Project.sln. That name is pretty boring and can be changed. The best way to do that is using the project command: project(hello_cmake)

Besides setting the project name, the command also sets the language support. The default is C and C++, so it is what we need for now. The reason why our CMakeLists.txt worked until now is that CMake will implicitly add a project(Project) if we don’t use the command. If we now clean out our _build directory and generate for Visual Studio, we’ll get a hello_cmake.sln instead of the Project.sln.

Conclusion

Our CMakeLists.txt begins to look like a proper project file:

cmake_minimum_required(VERSION 3.6) # The project name project(hello_cmake) # The sources shared between the main program and the tests set (PROJECT_SOURCES hello.cpp hello.h) # The main program add_executable(prog main.cpp ${PROJECT_SOURCES}) # The tests add_executable(tests testmain.cpp ${PROJECT_SOURCES})

See the current state of our little project here on GitHub.

Next up in my CMake blog series is the CMake Project Structure.