add_library()必须显式指定STATIC关键字,否则默认生成动态库,跨平台行为不可靠;静态库需正确配置PIC、依赖和头文件安装,且无法导出符号。

add_library() 必须显式指定 STATIC 关键字
不写 STATIC,CMake 默认生成 SHARED(动态库)或根据策略推断,这在跨平台时尤其不可靠。比如写 add_library(mylib src/a.cpp),在 Windows 上可能生成 .dll,Linux/macOS 上生成 .so,完全不是你想要的静态库。
正确写法必须明确声明:
add_library(mylib STATIC src/a.cpp src/b.cpp)
注意:STATIC 是关键字,不是字符串,不能加引号;也不能和 INTERFACE 或 OBJECT 混用。
静态库无法直接链接到可执行文件(除非显式 target_link_libraries)
很多人以为只要 add_library 写了 STATIC,后续 target_link_libraries(exe mylib) 就能自动把目标文件全塞进去——其实不然。CMake 默认只传递依赖信息,真正链接行为取决于目标属性和链接器策略。
确保静态库被完整归档进最终二进制,需检查以下几点:
-
mylib本身没有设置POSITION_INDEPENDENT_CODE OFF(默认是ON,但静态库通常不需要 PIC,可设为OFF以减小体积和避免重定位警告) - 链接目标(如
exe)未启用ENABLE_EXPORTS(该属性对静态库无意义,设了反而可能触发奇怪的符号导出逻辑) - 若
mylib依赖其他库(如Threads::Threads),这些依赖不会自动“扁平化”进libmylib.a;它们仍需在target_link_libraries(exe ...)中单独列出
install() 静态库时要同时安装头文件(否则下游无法使用)
只装 libmylib.a 没用——调用方连 #include "mylib.h" 都会失败。CMake 不会自动推断头文件安装路径。
典型做法是分两步:
install(TARGETS mylib ARCHIVE DESTINATION lib) # .a 文件走 ARCHIVE 类型 <p>install(FILES include/mylib.h DESTINATION include/mylib)</p>
如果头文件分散在多目录,建议用 install(DIRECTORY ...);另外,别忘了在 mylib 的 target_include_directories() 中正确设置 INTERFACE 路径,否则 target_link_libraries(exe mylib) 不会自动把头文件路径传给 exe。
STATIC 库无法导出符号(__declspec(dllexport) / visibility=hidden 失效)
这是最容易被忽略的语义差异:静态库本质是一堆 .o/.obj 打包成的归档文件,所有符号在链接阶段才解析。因此你在源码里写的 __declspec(dllexport)、__attribute__((visibility("default"))) 全部被忽略,编译器压根不处理。
这意味着:
- 不要在静态库头文件中条件定义
MYLIB_API宏来控制 dllexport/dllimport - 也不用担心符号隐藏问题——静态链接下所有可见符号(非
static或匿名命名空间内)默认都参与链接 - 若想控制哪些符号进入最终可执行文件,得靠链接器脚本或
-Wl,--exclude-libs等手段,在静态库构建阶段做不了
换句话说,静态库的“接口”仅由头文件声明 + 实现文件内容共同定义,没有任何运行时导出机制参与。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











