You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
)

包维护者的操作流程

  1. 分别构建静态、动态库版本,指定不同的临时安装目录:

    # 构建动态库并安装到临时目录
    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
    
  2. 合并两个临时目录的内容到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包的对应路径
  3. 补充顶层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,包维护者可以通过指定不同的安装前缀,分别构建静态、动态库,再合并产物:

  1. 构建动态库:
    cmake -S . -B build-shared -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr/lib/foobar/shared
    cmake --build build-shared --target install
    
  2. 构建静态库:
    cmake -S . -B build-static -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=/usr/lib/foobar/static
    cmake --build build-static --target install
    
  3. 合并两个前缀下的内容到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 14:25:23