如何用CMake将静态库私有链接到共享库并控制符号暴露?
实现静态库私有链接到共享库并控制符号暴露
完全可行,核心需要完成三件事:将静态库私有链接到共享库、精准控制共享库暴露的符号、让依赖共享库的应用能访问静态库的头文件。下面是修正后的完整实现方案和示例:
1. 修正基础CMake配置
你原代码里的静态库定义写法有误,每个静态库需要单独命名:
# 定义三个静态库 add_library(Static1 STATIC static1.cpp) add_library(Static2 STATIC static2.cpp) add_library(Static3 STATIC static3.cpp) # 定义目标共享库 add_library(Shared SHARED shared.cpp)
2. 私有链接静态库
用PRIVATE关键字链接静态库,确保这些静态库的代码被打包进Shared.so,但不会让依赖Shared的目标(比如你的app)直接链接这些静态库:
target_link_libraries(Shared PRIVATE Static1 Static2 Static3)
3. 控制共享库暴露的符号
默认情况下,静态库的符号会被共享库暴露出来,我们需要隐藏这些符号,只对外暴露你指定的接口,有两种常用方式:
方式一:用GenerateExportHeader管理导出符号
通过生成导出宏标记需要暴露的符号,其余符号默认隐藏:
include(GenerateExportHeader) # 生成导出头文件,宏前缀为MY_SHARED generate_export_header(Shared SHARED_NAME MY_SHARED EXPORT_MACRO_NAME MY_SHARED_EXPORT EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/include/my_shared_export.h ) # 让Shared库能找到生成的导出头 target_include_directories(Shared PRIVATE ${CMAKE_BINARY_DIR}/include)
然后在shared.cpp中,显式导出你想让外部访问的静态库符号:
#include "my_shared_export.h" // 导出Static1中的func1函数给外部使用 extern "C" MY_SHARED_EXPORT void func1(); // 导出Static2中的类给外部使用 class MY_SHARED_EXPORT Static2Class { // 类定义... };
方式二:链接器选项强制隐藏符号(更彻底)
针对GCC/Clang,用编译和链接选项强制隐藏所有未显式导出的符号:
# 编译时设置默认符号可见性为隐藏 target_compile_options(Shared PRIVATE -fvisibility=hidden) # 链接时排除所有静态库的符号,不让它们自动暴露 target_link_options(Shared PRIVATE -Wl,--exclude-libs,ALL)
4. 让应用访问静态库的头文件
要让app能直接包含静态库的头文件,需要把静态库的头文件路径传递给Shared库的依赖目标:
# 先给每个静态库设置头文件目录(PUBLIC表示暴露给依赖它的目标) target_include_directories(Static1 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/static1_include) target_include_directories(Static2 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/static2_include) target_include_directories(Static3 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/static3_include) # 让Shared库把静态库的头文件路径传递给依赖它的app target_include_directories(Shared INTERFACE $<TARGET_PROPERTY:Static1,INTERFACE_INCLUDE_DIRECTORIES> $<TARGET_PROPERTY:Static2,INTERFACE_INCLUDE_DIRECTORIES> $<TARGET_PROPERTY:Static3,INTERFACE_INCLUDE_DIRECTORIES> )
这样app链接Shared后,就能直接包含静态库的头文件并调用已导出的符号:
// app.cpp #include "static1.h" #include "static2.h" int main() { func1(); // 调用Static1中被Shared导出的函数 Static2Class obj; // 使用Static2中被Shared导出的类 return 0; }
完整CMake示例
cmake_minimum_required(VERSION 3.16) project(SharedStaticDemo) # 定义静态库及头文件路径 add_library(Static1 STATIC static1.cpp) target_include_directories(Static1 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/static1_include) add_library(Static2 STATIC static2.cpp) target_include_directories(Static2 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/static2_include) add_library(Static3 STATIC static3.cpp) target_include_directories(Static3 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/static3_include) # 定义共享库 add_library(Shared SHARED shared.cpp) # 生成导出头文件 include(GenerateExportHeader) generate_export_header(Shared SHARED_NAME MY_SHARED EXPORT_MACRO_NAME MY_SHARED_EXPORT EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/include/my_shared_export.h ) # 设置符号隐藏选项 target_compile_options(Shared PRIVATE -fvisibility=hidden) target_link_options(Shared PRIVATE -Wl,--exclude-libs,ALL) # 私有链接静态库并传递头文件路径 target_link_libraries(Shared PRIVATE Static1 Static2 Static3) target_include_directories(Shared PRIVATE ${CMAKE_BINARY_DIR}/include INTERFACE $<TARGET_PROPERTY:Static1,INTERFACE_INCLUDE_DIRECTORIES> $<TARGET_PROPERTY:Static2,INTERFACE_INCLUDE_DIRECTORIES> $<TARGET_PROPERTY:Static3,INTERFACE_INCLUDE_DIRECTORIES> ) # 构建应用 add_executable(app app.cpp) target_link_libraries(app Shared)
注意事项
- 静态库的符号必须在
Shared库中显式导出,否则会因符号隐藏无法被app调用 - MSVC环境下符号隐藏逻辑略有不同,需用
__declspec(dllexport)/__declspec(dllimport)配合GenerateExportHeader - 如果静态库本身有复杂的依赖关系,需确保这些依赖也被正确处理
内容的提问来源于stack exchange,提问作者at_123
相关产品推荐
相关产品推荐

