CMake:正确处理静态库与动态库的方法
如何在CMake中同时导出静态和动态库变体适配Debian/Ubuntu包结构
针对你提到的问题——既要让Debian/Ubuntu的libfoobar-dev包同时包含静态、动态库的CMake导出目标,又不违背CMake最佳实践(避免重复定义库目标),可以通过以下几种方式解决:
1. 利用CMake组件机制分离静态/动态库的导出
核心思路是通过BUILD_SHARED_LIBS控制单目标的库类型,再用**组件(Component)**分别导出不同变体的Targets文件,避免覆盖。
库作者的CMakeLists.txt写法
add_library(foobar src/foobar.cpp) # 公共头文件路径配置 target_include_directories(foobar PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include> ) # 根据BUILD_SHARED_LIBS的值,为静态/动态库设置不同的导出规则 if(BUILD_SHARED_LIBS) install(TARGETS foobar EXPORT FoobarTargets_shared COMPONENT shared_lib LIBRARY DESTINATION lib INCLUDES DESTINATION include ) else() install(TARGETS foobar EXPORT FoobarTargets_static COMPONENT static_lib ARCHIVE DESTINATION lib INCLUDES DESTINATION include ) endif() # 导出对应的Targets文件,文件名区分静态/动态 install(EXPORT FoobarTargets_$<IF:$<BOOL:${BUILD_SHARED_LIBS}>,shared,static> FILE FoobarTargets_$<IF:$<BOOL:${BUILD_SHARED_LIBS}>,shared,static>.cmake NAMESPACE Foobar:: DESTINATION lib/cmake/Foobar COMPONENT dev )
包维护者的操作流程
分别构建静态、动态库版本,指定不同的临时安装目录:
# 构建动态库并安装到临时目录 mkdir build-shared && cd build-shared cmake .. -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr make && make DESTDIR=../tmp-shared install # 构建静态库并安装到临时目录 mkdir ../build-static && cd ../build-static cmake .. -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=/usr make && make DESTDIR=../tmp-static install合并两个临时目录的内容到
libfoobar-dev包的路径:- 将
tmp-shared/usr/include、tmp-shared/usr/lib/cmake复制到dev包的对应路径 - 将
tmp-static/usr/lib/libfoobar.a、tmp-static/usr/lib/cmake/Foobar/FoobarTargets_static.cmake复制到dev包的对应路径
- 将
补充顶层
FoobarConfig.cmake(放在lib/cmake/Foobar下),让find_package能识别两个变体:# 若有依赖,添加find_dependency语句 # find_dependency(SomeDependency) # 按需包含静态/动态库的Targets文件 include("${CMAKE_CURRENT_LIST_DIR}/FoobarTargets_shared.cmake" OPTIONAL) include("${CMAKE_CURRENT_LIST_DIR}/FoobarTargets_static.cmake" OPTIONAL)
2. 用CMAKE_INSTALL_PREFIX区分构建产物
如果不想修改库的CMakeLists.txt,包维护者可以通过指定不同的安装前缀,分别构建静态、动态库,再合并产物:
- 构建动态库:
cmake -S . -B build-shared -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr/lib/foobar/shared cmake --build build-shared --target install - 构建静态库:
cmake -S . -B build-static -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=/usr/lib/foobar/static cmake --build build-static --target install - 合并两个前缀下的内容到
libfoobar-dev的标准路径:- 将动态库的
libfoobar.so、FoobarTargets.cmake重命名为FoobarTargets_shared.cmake - 将静态库的
libfoobar.a、FoobarTargets.cmake重命名为FoobarTargets_static.cmake - 统一放到
/usr/lib和/usr/lib/cmake/Foobar目录下,再补充顶层的FoobarConfig.cmake(同上)
- 将动态库的
关键注意点
- 避免在CMakeLists.txt中同时定义
foobar和foobar_static两个目标,这会增加维护成本,违背DRY原则 - 确保导出的Targets文件使用不同的文件名,防止覆盖
- 顶层
FoobarConfig.cmake要兼容用户只需要其中一种库变体的场景,用OPTIONAL关键字避免报错
内容的提问来源于stack exchange,提问作者Max Dmitrichenko
相关产品推荐
相关产品推荐

