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
mylibitself 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:
| Keyword | What it means |
|---|---|
PRIVATE | Only this target needs it. |
INTERFACE | Only the targets that use this target need it. |
PUBLIC | Both. 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.txtis full of bareinclude_directories(),add_definitions()andlink_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