Lesson 7 · Targets: the core model

What is a target

A target is one thing your project builds: an executable or a library. You give each target its sources and its settings. That is the whole system.

A target is one thing your project builds. An executable, a library, or a set of settings with no code of its own.

This is the idea the rest of the course is built on. It is simple. Once you see it, CMake stops being a list of commands.

How a target works

A target has:

  • sources, the files that get compiled,
  • settings, what the target needs to compile and link correctly,
  • a name, so other targets can use it.
app (an executable)
  • sources:   main.cpp
  • needs:     mylib

mylib (a static library)
  • sources:   mylib.cpp
  • needs:     C++20, its own headers
  • gives:     app uses it

Your job in CMake is to give each target its sources and its settings. CMake does the rest.

Old style vs this style

Older CMake set everything globally. Every setting applied to every target below it, whether it made sense or not:

Don't do this
# OLD. Global. Affects every target below these lines. include_directories(include) add_compile_options(-Wall) link_libraries(mylib) add_executable(app main.cpp)

The style this course teaches attaches each setting to the target that needs it:

Do this
# Attached to exactly one target. target_include_directories(mylib PUBLIC include) target_compile_options(app PRIVATE -Wall) target_link_libraries(app PRIVATE mylib) add_executable(app main.cpp)

Same result, but nothing leaks. -Wall is only for app. include/ is only for mylib and the targets that use it. Third-party code you add later is never affected.

Why

Global settings are a time bomb. They reach targets you do not own, they break when you move a line, and nobody can tell who needed what. A setting attached to one target can only ever affect that target and its users. This is why the official tutorial teaches the target style.

Requirements travel with the target

Here is the part that makes targets powerful. When app links mylib, app automatically gets whatever mylib marked as PUBLIC or INTERFACE:

  • the folders where mylib’s public headers live,
  • the libraries mylib itself needs,
  • the C++ standard mylib’s headers require.

You write each requirement once, on the target that owns it. Every target that uses it gets it for free.

These traveling requirements are called usage requirements. The next two lessons are about the three keywords that control them, PRIVATE, PUBLIC, and INTERFACE, so let us define them right now:

KeywordWhat it means
PRIVATEOnly this target needs it.
INTERFACEOnly the targets that use this target need it.
PUBLICBoth. This target needs it, and its users need it too.

Tip

If you remember one thing from this course, remember this. Give every requirement to the target that owns it, with the smallest scope that works. Everything else is details.

Pitfall

If your CMakeLists.txt is full of bare include_directories(), add_definitions() and link_libraries(), you are writing the old style. It still works, but it fights you as the project grows. Lesson 30 lists these commands and their replacements.

Read the docs