add_library的库名必须是纯标识符且全局唯一,不可含路径或重复命名;源文件路径须相对于当前CMakeLists.txt;STATIC/SHARED/OBJECT类型语义不同,INTERFACE库仅传播编译属性、不可含源文件。

add_library 生成的库名不能带路径
你写 add_library(./src/mylib STATIC src/a.cpp) 会报错,CMake 不允许库名含斜杠或相对路径。库名必须是纯标识符,比如 mylib 或 core_utils。路径信息只出现在源文件列表里,不参与目标命名。
常见错误现象:CMake Error: add_library cannot create target "xxx" because another target with the same name already exists,往往是因为重复调用 add_library 且名字相同(比如在不同子目录里都用了 add_library(utils ...)),而 CMake 的 target 名是全局唯一的。
- 库名要唯一,建议加前缀,比如
projectname_utils - 如果想按目录组织逻辑,用
add_subdirectory()隔离作用域,而不是靠路径命名 - 别把
add_library放在循环里动态生成同名 target,CMake 不支持
STATIC、SHARED、OBJECT 三种类型差异很关键
类型选错会导致链接失败或运行时找不到符号。比如你写了 add_library(mylib SHARED src/impl.cpp),但头文件里声明了 inline 函数或模板,那调用方很可能编译不过——因为 SHARED 库默认不导出这些符号,而 STATIC 才天然支持内联展开。
使用场景判断:
-
STATIC:适合工具链内部复用、避免动态依赖,如gtest静态链接进测试可执行文件 -
SHARED:需要运行时加载、多进程共享、或明确要求 DLL/SO 文件(如插件架构) -
OBJECT:不是真正“库”,只是预编译对象集合,常用于避免重复编译,比如add_library(common_objs OBJECT a.cpp b.cpp),再被多个add_executable通过$<TARGET_OBJECTS:common_objs>引用
源文件路径写法必须是相对于当前 CMakeLists.txt
假设你的 CMakeLists.txt 在 project/src/ 目录下,那么 add_library(mylib src/a.cpp include/b.h) 是错的——src/a.cpp 实际路径是 project/src/src/a.cpp。CMake 不自动补前缀,所有路径都是字面量解释。
正确做法:
- 用
file(GLOB ...)或set(SOURCES ...)显式列出文件,并确保路径对齐 - 跨目录引用时,用
${CMAKE_CURRENT_SOURCE_DIR}/../include这类变量拼接,别硬写../include - 避免通配符滥用,比如
add_library(mylib *.cpp)在子目录里可能漏文件,也不利于增量构建
INTERFACE 库不能有源文件,但必须设好 INTERFACE_INCLUDE_DIRS
写 add_library(myiface INTERFACE) 后直接 target_sources(myiface PRIVATE inc/a.h) 会触发 fatal error:INTERFACE library cannot have sources。INTERFACE 库只传播编译属性,不编译。
典型用途是封装头文件 + 编译选项,比如封装 spdlog:
add_library(spdlog_iface INTERFACE)
target_include_directories(spdlog_iface INTERFACE ${SPDLOG_INCLUDE_DIR})
target_compile_features(spdlog_iface INTERFACE cxx_std_17)
然后其他 target 用 target_link_libraries(other_target PRIVATE spdlog_iface) 就能继承头路径和语言标准。
容易踩的坑:
-
INTERFACE库不能用target_sources,也不能用PRIVATE或PUBLIC关键字设置源文件 -
INTERFACE_INCLUDE_DIRECTORIES已废弃,必须用target_include_directories(... INTERFACE ...) - 如果忘了设
INTERFACE属性,下游 target 就找不到头文件,报fatal error: xxx.h: No such file or directory
INTERFACE 关键字,下游就断;而 STATIC/SHARED 库一旦编译成功,后续问题更多出在链接阶段。这两类用法背后是完全不同的依赖模型,混用会直接卡住构建。


















