Table of Contents

CMake Linking

target_link_libraries(target PRIVATE|PUBLIC|INTERFACE libs) links a target to libraries. The keyword controls visibility: PRIVATE (only target needs it), PUBLIC (target and consumers), INTERFACE (only consumers).

Basic linking:

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE mylib)

This links the executable myapp to the library mylib. The library is private — consumers of myapp don't need it.

Linking multiple libraries:

target_link_libraries(myapp PRIVATE mylib1 mylib2 mylib3)
target_link_libraries(myapp PUBLIC system_lib)

Modern CMake uses target names, not raw flags or paths:

find_package(Eigen3 REQUIRED)
target_link_libraries(myapp PRIVATE Eigen3::Eigen3)
 
find_package(OpenGL REQUIRED)
target_link_libraries(myapp PRIVATE OpenGL::GL)

Avoid the old style:

# OLD: don't do this
target_link_libraries(myapp "-lm")
link_directories(/usr/lib)
include_directories(/usr/include/eigen3)
 
# NEW: use targets
find_package(Eigen3 REQUIRED)
target_link_libraries(myapp PRIVATE Eigen3::Eigen3)

Visibility keywords:

Example with header-only library:

add_library(headeronly INTERFACE)
target_include_directories(headeronly INTERFACE include/)
target_link_libraries(headeronly INTERFACE some_dependency)
 
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE headeronly)

Transitive linking:

add_library(liba a.cpp)
add_library(libb b.cpp)
target_link_libraries(libb PUBLIC liba)  # libb depends on liba publicly
 
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE libb)  # myapp gets both libb and liba

With PUBLIC, consumers automatically get transitive dependencies. This propagates include paths, compile definitions, and linking requirements.

Use target_include_directories() and target_compile_options() for parallel configuration. They follow the same PRIVATE|PUBLIC|INTERFACE pattern.

Modern CMake means target-based linking. Each target declares its dependencies explicitly, making builds predictable and modular.