Lesson 10 · Targets: the core model

Linking targets

How one target says it uses another. What PRIVATE, PUBLIC and INTERFACE mean when linking, and how requirements flow through a chain.

Building libraries is only half the story. You have to connect them. target_link_libraries is how one target says “I use this other target”.

The basic form

target_link_libraries(app PRIVATE calc)

Translation. The executable app uses the library calc, and nobody needs to know about that except app. Now the keywords, same as last lesson:

  • PRIVATE. Only app needs calc. If some other target links app, it does not get calc.
  • PUBLIC. app uses calc, and calc is part of what app offers. If another target links app, it must also get calc. Use this when calc’s headers appear in app’s public headers.
  • INTERFACE. app does not link calc itself, but its consumers must. The header-only case from lesson 13.

The chain that just works

app  ──PRIVATE──►  calc  ──PRIVATE──►  math
math/CMakeLists.txt
add_library(math STATIC math.cpp)
calc/CMakeLists.txt
add_library(calc STATIC calc.cpp) target_link_libraries(calc PRIVATE math) # calc needs math. Consumers do not.
app/CMakeLists.txt
add_executable(app main.cpp) target_link_libraries(app PRIVATE calc) # app gets calc and math automatically

app’s final link line includes both libcalc.a and libmath.a, even though app never mentions math. Why is that correct? Because calc.cpp calls math functions, and the static library copies them into the executable. The linker has to see math when it links app.

Why

Requirements flow through the chain, so you state each connection once. In the old style you maintained link_libraries(app math), link_libraries(calc math), and the ordering, by hand. The moment the chain grew, you got “undefined reference” errors forever. CMake builds the link line from the graph.

The classic static-library pitfall

With raw make, a static library must appear after the objects that use it, and circular dependencies (A uses B, B uses A) are a nightmare. CMake handles ordering and repetition for you. That is a big reason projects work with CMake even when hand-written link lines fail.

Which one do I use

Ask one question. Does this library appear in my public headers?

app.h
#include "calc/calc.h" // is this line in a public header?
  • Yes. Use PUBLIC, or INTERFACE if you do not link it yourself.
  • No. Use PRIVATE.

When in doubt, start with PRIVATE. It is the smallest scope that works.

Pitfall

You cannot link a target that does not exist yet. add_library(calc ...) must come before target_link_libraries(app ... calc). In project layout terms, add the library’s directory before the app’s (lesson 15).

Tip

A name that is not a target (for example m, pthread, dl) is passed straight to the linker. Handy for system libraries on Linux, target_link_libraries(app PRIVATE m). But prefer imported targets when a package provides them (lesson 14). Threads::Threads is always right, bare pthread is not.

Read the docs