Build system with modern CMake

A while ago, I had an article related to CMake build system. I wanted to get back to that topic and make something simpler without being part of a series.

I also did some very short explanatory video on this topic, on YouTube here. That video provides you a preview of what CMake is and how it works so you may want to check that out first. It’s not mandatory.

One more reason why I wanted to write this article is that, since I’ve started a new project, I was reminded about the importance of a good build system. If your build system is bad, your life is probably going to be hell going forward.

Prerequisites

So, here is a list of prerequisites for what I’m going to present below:

  • cmake
  • make/ninja or any other generator
  • project with source and header files

If you don’t know how to get the prerequisites above, you can check out my old article. I also explain there how to run CMake after everything is done.

For the project, we will use a dummy one with the following directory structure:

We are going to discuss only about CMakeLists.txt, just giving you some info on how to get started with CMake.

CMakeLists.txt

For our example, we are going to use CMake to:

  • generate an executable from Dummy.cpp and main.cpp
  • generate a shared library from Dummy.cpp
  • generate a static library from Dummy.cpp

We need:

  • the minimum version of cmake
  • the project
  • an executable target
  • make the header files from include directory available
  • C++20 features

This is how the CMakeLists.txt file will look like for our project. I added comments to each line just so you know what we are doing and why.

# minimum version of CMake needed for the project
cmake_minimum_required(VERSION 3.20)

# name of the project
project(DummyProject)

# variables with the name of the artifact(the executable)
set(EXECUTABLE_NAME ${PROJECT_NAME}_EXE)

# create executable target
add_executable(${EXECUTABLE_NAME})
# specify the source files for the executable
# these source files are private to the executable
target_sources(${EXECUTABLE_NAME}
	PRIVATE
		src/Dummy.cpp
		src/main.cpp
)
# include directories for our executable
# every include file in this directory can be used for the executable only
target_include_directories(${EXECUTABLE_NAME}
	PRIVATE
		include/
)

# compile features for the executable
# we instruct the compiler to use C++20 standard
target_compile_features(${EXECUTABLE_NAME}
	PRIVATE
		cxx_std_20
)

Please note that packaging and installing is not done in this example. Maybe I will cover that in future articles.

Building

Now that we have the CMake configuration ready and our project, we can go ahead and build it. This is done in 2 steps: 1.CMake configuration, 2.Actual build with a generator. The most used generator is Make, so you can about this as follows:

mkdir build && cd build && cmake .. && make

CMake needs the path with the CMakeLists.txt file. By using it without specifying any generator(which can be done with “-G” option), it will assume Make as generator.

If you want to use other generator, such as ninja, you would need to do:

mkdir build && cd build && cmake -G "Ninja" .. && ninja

Notice that I’ve instructed CMake to use Ninja as a generator in the configuration and then I used Ninja for the actual build.

Some tips

Basically, this example is only to get you started with modern cmake.

If you check out the old article, you can see that there are some differences.

What is good to keep in mind for modern cmake:

  • use CMake version above 3.0.0 – preferably the latest
  • don’t add global options – every target should have it’s own options. i.e. don’t use include_directories, link_directories, link_libraries etc
  • don’t use file(GLOB)
  • don’t touch CMAKE_CXX_FLAGS

These hints and many more are present in a very good gist that I found on the internet. You can check it out here.

Full documentation for CMake can be found here.

1 thought on “Build system with modern CMake”

  1. Pingback: Simple build system using CMake - cppdev

Leave a Reply

%d bloggers like this: