CMake 库目标
本章将介绍如何使用 CMake 创建和管理库目标,包括静态库、动态库和接口库。
库的类型
| 类型 | 关键字 | 特点 |
|---|---|---|
| 静态库 | STATIC | 编译时链接,体积大,性能好 |
| 动态库 | SHARED | 运行时加载,体积小,更新方便 |
| 模块库 | MODULE | 运行时动态加载(插件) |
| 对象库 | OBJECT | 编译但不归档,供其他目标使用 |
| 接口库 | INTERFACE | 仅头文件,无编译输出 |
创建静态库
add_library(mylib STATIC
src/lib.cpp
src/helper.cpp
)
target_include_directories(mylib
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
创建动态库
add_library(mylib SHARED
src/lib.cpp
src/helper.cpp
)
# 设置导出符号
target_compile_definitions(mylib PRIVATE MYLIB_EXPORTS)
符号导出
mylib_export.h
#ifdef MYLIB_EXPORTS
#define MYLIB_API __attribute__((visibility("default")))
#else
#define MYLIB_API
#endif
MYLIB_API void my_function();
Windows DLL 导出
#ifdef _WIN32
#ifdef MYLIB_EXPORTS
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif
#else
#define MYLIB_API
#endif
创建接口库
接口库用于纯头文件库。
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# 设置编译特性
target_compile_features(mylib INTERFACE cxx_std_17)
使用库
链接库到可执行文件
add_library(mylib STATIC src/lib.cpp)
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
链接可见性
target_link_libraries(target
PRIVATE lib_internal # 仅当前目标使用
PUBLIC lib_public # 当前目标和依赖者都可使用
INTERFACE lib_interface # 仅依赖者可使用
)
示例
# 库 A 依赖库 B
add_library(B ...)
add_library(A ...)
# 如果 B 的头文件出现在 A 的公共头文件中
target_link_libraries(A PUBLIC B)
# 如果 B 仅在 A 的实现中使用
target_link_libraries(A PRIVATE B)
对象库
对象库编译源文件但不创建库文件:
add_library(common_obj OBJECT
src/common.cpp
src/utils.cpp
)
# 静态库使用对象库
add_library(mylib STATIC $<TARGET_OBJECTS:common_obj>)
# 动态库使用同一对象库
add_library(mylib_shared SHARED $<TARGET_OBJECTS:common_obj>)
库的安装
导出配置
add_library(mylib src/lib.cpp)
# 安装库和头文件
install(TARGETS mylib
EXPORT mylibTargets
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
install(DIRECTORY include/
DESTINATION include
)
# 导出目标
install(EXPORT mylibTargets
FILE mylibTargets.cmake
NAMESPACE mylib::
DESTINATION lib/cmake/mylib
)
版本控制
set_target_properties(mylib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1 # API 版本
)
这将生成:
libmylib.so.1.0.0(实际文件)libmylib.so.1->libmylib.so.1.0.0libmylib.so->libmylib.so.1
库属性
常用属性
set_target_properties(mylib PROPERTIES
OUTPUT_NAME "mylib" # 输出文件名
VERSION "1.0.0" # 版本号
SOVERSION "1" # SO版本号
POSITION_INDEPENDENT_CODE ON # 生成位置无关代码
CXX_VISIBILITY_PRESET hidden # 默认隐藏符号
VISIBILITY_INLINES_HIDDEN ON # 隐藏内联函数符号
)
导入库属性
对于预编译的库,可以使用 IMPORTED 属性:
add_library(prebuilt STATIC IMPORTED)
set_target_properties(prebuilt PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libprebuilt.a"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include"
)
小结
本章我们学习了:
- 库的类型:静态库(STATIC)、动态库(SHARED)、模块库(MODULE)、对象库(OBJECT)、接口库(INTERFACE)
- 创建库:使用 add_library 命令创建各种类型的库
- 符号导出:跨平台的 DLL/SO 符号导出机制
- 链接可见性:PUBLIC、PRIVATE、INTERFACE 三种可见性控制
- 库安装:导出配置和版本控制
- 库属性:常用的库属性设置
练习
- 创建一个静态库并链接到可执行文件
- 实现跨平台的动态库导出
- 创建一个纯头文件库
- 使用对象库在多个目标间共享编译结果
- 配置库的版本控制和安装规则