Lesson 14 · Targets: the core model
Imported targets
What imported targets are, why find_package hands you Threads::Threads instead of raw flags, and how to use them safely.
So far you have built your own targets. Imported targets are targets that already exist, created by CMake or by a package, and they represent a library that is already built and installed on the system. You link them like your own.
The canonical example
find_package(Threads REQUIRED)
target_link_libraries(app PRIVATE Threads::Threads)
Threads::Threads is an imported target that represents the system’s threading library. Look at what CMake did for you:
- On Linux, it links
pthreadand adds-pthreadto compilation. - On Windows, it links nothing. Threads are in the runtime.
- On macOS, it works as-is.
One line, every platform, no if(WIN32) branches. That is the point of imported targets. They carry the platform knowledge inside them, so you do not have to.
Why
The old way was
find_package(Threads)plustarget_link_libraries(app PRIVATE ${CMAKE_THREAD_LIBS_INIT}). A variable set somewhere, used somewhere else. Imported targets turn “the right flags” into a named thing you link. They are the same target model you already know, applied to system packages.
Where they come from
Three sources:
- CMake itself.
find_packagemodules ship imported targets, such asThreads::Threads,OpenMP::OpenMP_CXX,ZLIB::ZLIB,CURL::libcurl,Boost::headers. - A config package. A library you installed and found with
find_package(lesson 17), for examplefmt::fmt,spdlog::spdlog. - You.
add_library(... IMPORTED)by hand. Rare, and only when nothing else works.
The naming convention is Namespace::Name, Threads::Threads, fmt::fmt. The namespace is what keeps names from clashing.
Checking that a package was found
find_package(fmt CONFIG QUIET)
if(fmt_FOUND)
target_link_libraries(app PRIVATE fmt::fmt)
else()
message(FATAL_ERROR "fmt is required. Install it or set CMAKE_PREFIX_PATH.")
endif()
Or, more robust, check that the target exists:
if(TARGET fmt::fmt)
target_link_libraries(app PRIVATE fmt::fmt)
endif()
Not every find_package gives targets
Some Find*.cmake modules only set variables (FOO_LIBRARIES, FOO_INCLUDE_DIRS). Usually the old ones, and lesson 17 explains module versus config mode. The rule:
# If the package offers an imported target, use it.
target_link_libraries(app PRIVATE fmt::fmt)
# Only fall back to variables when no target exists.
target_link_libraries(app PRIVATE ${FOO_LIBRARIES})
target_include_directories(app PRIVATE ${FOO_INCLUDE_DIRS})
Pitfall
Do not fight the package. Linking both the imported target and the raw variables double-links. Pick the target when it exists. Imported targets are built exactly so you do not touch
_LIBRARIESor_INCLUDE_DIRSat all.
Tip
Curious what an imported target contains? Run
cmake --build build --target app -vand look at the final compile and link flags (lesson 27). You will see the platform logic,-pthread,/MD, that the imported target added for you.
Read the docs