使用CMake时如何避免在每个源文件重复定义共享库导出宏?
问题描述
我正把基于CMake构建的C项目移植到Windows Visual Studio环境。和Linux/g自动导出所有符号不同,MSVC只导出显式指定的符号。尝试用CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS后仍有链接错误,且得知这个选项并非总能生效,更优的做法是仅导出必要符号(利于优化和加快链接速度)。
我通过在头文件顶部添加以下代码解决了问题:
#if defined(_WIN64) #define EXPORT __declspec(dllexport) #elif defined(__linux__) #define EXPORT __attribute__((visibility("default"))) #endif
并将类声明改为class EXPORT MyClass {...,构建恢复正常。但每个文件都加这段代码太繁琐,后续修改也麻烦。想知道能不能把这个宏定义统一管理在一处,还是必须在每个含需导出符号的文件中添加?
解决方案
方法一:创建公共头文件集中管理
新建一个如export_macros.h的公共头文件,把宏定义写入其中,再在所有需要导出符号的头文件顶部#include "export_macros.h"即可。这种方式直观易维护,还能添加头文件保护避免重复包含:
#pragma once #if defined(_WIN64) #if defined(MY_PROJECT_BUILDING_DLL) #define EXPORT __declspec(dllexport) #else #define EXPORT __declspec(dllimport) #endif #elif defined(__linux__) #define EXPORT __attribute__((visibility("default"))) #else #define EXPORT #endif
补充说明:如果项目同时存在生成DLL的目标和使用该DLL的目标,需通过CMake给DLL目标定义专属宏区分导出/导入场景:
target_compile_definitions(your_dll_target PRIVATE MY_PROJECT_BUILDING_DLL)
方法二:通过CMake直接注入宏定义
无需额外头文件,利用CMake的target_compile_definitions结合生成器表达式,针对不同平台给目标注入宏:
target_compile_definitions(your_target PRIVATE $<$<PLATFORM_ID:Windows>:EXPORT=__declspec(dllexport)> $<$<PLATFORM_ID:Linux>:EXPORT=__attribute__((visibility("default")))> )
这种方式把宏逻辑放到CMake脚本中,无需修改代码,但复杂逻辑的维护性不如头文件方案。
方法三:使用CMake官方GenerateExportHeader模块
CMake自带的GenerateExportHeader模块可自动生成适配跨平台的导出宏头文件,无需手动编写宏定义:
- 在CMakeLists.txt中添加配置:
include(GenerateExportHeader) generate_export_header(your_dll_target BASE_NAME MY_PROJECT EXPORT_MACRO_NAME MY_PROJECT_EXPORT EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/my_project_export.h )
- 在需要导出符号的头文件中包含生成的头:
#include "my_project_export.h" class MY_PROJECT_EXPORT MyClass {...};
该模块会自动处理Windows的dllexport/dllimport、Linux的符号可见性,适配多平台,是CMake官方推荐的跨平台符号导出方案。
内容的提问来源于stack exchange,提问作者Chris Gnam

