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:
PRIVATE — only myapp uses it at link timePUBLIC — myapp uses it, and anything linking to myapp also needs itINTERFACE — only consumers of myapp need it (used for header-only libraries)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.