必须先用add_executable或add_library声明目标,再调用target_link_libraries绑定;目标名大小写、拼写必须完全一致,否则链接静默失效,运行时报undefined symbol;推荐优先使用find_package + imported target(如fmt::fmt)自动管理头文件与链接。

target_link_libraries 必须绑定已定义的目标
没声明 add_executable 或 add_library,target_link_libraries 就是空转——它不报错,但链接关系根本不会生效。常见错误是写成 target_link_libraries(myapp PRIVATE ${OpenCV_LIBS}),却漏了前面的 add_executable(myapp main.cpp)。
目标名大小写、下划线、拼写必须完全一致:写成 myApp 或 my-app,和 add_executable 里定义的不匹配,运行时大概率出现 undefined symbol 错误,且 CMake 配置阶段不会提示。
链接系统库优先用 find_package + imported target
比如 fmt、Eigen、spdlog 这类提供 fmtConfig.cmake 的库,直接用导入目标最干净:
find_package(fmt CONFIG REQUIRED) target_link_libraries(myapp PRIVATE fmt::fmt)
这种写法自动带入头文件路径、编译选项、链接库,不用再配 target_include_directories 或手动拆 ${fmt_LIBRARIES}。
- 如果库只提供
FindXXX.cmake(如老版本 OpenCV),就得用find_package(OpenCV REQUIRED)+${OpenCV_LIBS},但变量名可能叫${OpenCV_LIBRARIES}或${OpenCV_LIBRARY},得查文档或看 CMake 输出确认 -
REQUIRED很关键:不加它,find_package找不到时静默失败,后面${OpenCV_LIBS}是空字符串,链接直接挂掉 - 避免用
include_directories全局污染;改用target_include_directories(myapp PRIVATE ${OpenCV_INCLUDE_DIRS})
链接本地静态库要写绝对路径,别碰 link_directories
link_directories 是全局作用域,会影响项目里所有 target,而且在多配置构建(Debug/Release)下极易出错。正确做法是把库路径写死进 target_link_libraries:
CMake 4.3.2 Windows x86_64 历史版本安装包,适合旧项目兼容、构建环境回退、CMakeLists.txt 迁移验证、Visual Studio/Ninja/Makefile 生成器测试和 C/C++ 项目维护。
target_link_libraries(myapp PRIVATE ${CMAKE_SOURCE_DIR}/third_party/zlib/lib/libz.a)
Linux/macOS 用 .a,Windows 用 .lib,动态库则用 .so / .dll ——但注意:.dll 在 Windows 上只参与链接,运行时还得确保在 PATH 里。
- 静态库路径建议用
${CMAKE_SOURCE_DIR}或${CMAKE_CURRENT_SOURCE_DIR},别用相对路径如../lib/libz.a,跨子目录会失效 - 如果库有依赖(比如
libfoo.a依赖libz.a),顺序不能反:target_link_libraries(myapp PRIVATE libfoo.a libz.a),否则链接器找不到符号 - 用
file(TO_CMAKE_PATH ...)处理 Windows 路径斜杠问题,避免硬写C:/libs/foo.lib
PRIVATE / PUBLIC / INTERFACE 影响头文件可见性
这个参数不是可选的“装饰”,它决定依赖是否透传给下游 target。比如你的库 mylib 用了 fmt,又导出了含 fmt::format 的头文件:
add_library(mylib src.cpp) target_link_libraries(mylib PUBLIC fmt::fmt) # 下游用 mylib 时自动获得 fmt 头文件 target_link_libraries(myapp PRIVATE mylib) # myapp 不需要知道 fmt
写错会导致编译失败:下游 target 报 fmt/format.h: No such file or directory。
-
PRIVATE:仅本 target 内部用,不透传 -
PUBLIC:本 target 用 + 所有链接它的 target 也能用(头文件 + 链接) -
INTERFACE:只透传,自己不用(比如纯头文件库entt)
多数情况从 PRIVATE 开始试,报头文件找不到再往上提一级。滥用 PUBLIC 会让依赖树越来越不可控。










