CMake 011:跨平台动态库编译
- [Bilibili 同步视频](#Bilibili 同步视频)
- [一、先踩第一个坑:Windows 动态库「只出 DLL,不出 LIB」](#一、先踩第一个坑:Windows 动态库「只出 DLL,不出 LIB」)
-
- [🔍 核心原理:Windows 与类 Unix 系统的本质差异](#🔍 核心原理:Windows 与类 Unix 系统的本质差异)
- 二、修复第一步:给头文件加上「导出声明」
- [三、第二个坑:调用方「被误导出」,生成多余 LIB](#三、第二个坑:调用方「被误导出」,生成多余 LIB)
-
- [正确逻辑:导出 / 导入必须分离](#正确逻辑:导出 / 导入必须分离)
- [CMake 神助攻:自动生成 `XLOG_EXPORTS`](#CMake 神助攻:自动生成
XLOG_EXPORTS)
- [四、第三个坑:Linux/macOS 不认识 Windows 语法](#四、第三个坑:Linux/macOS 不认识 Windows 语法)
-
- 终极跨平台兼容宏(三端通吃)
- [CMake 配合平台判断,彻底稳了](#CMake 配合平台判断,彻底稳了)
- 五、必看细节:编码格式导致的诡异报错
-
- [强制规范:统一用 UTF-8 with BOM](#强制规范:统一用 UTF-8 with BOM)
- 六、静态库怎么办?一行宏搞定兼容
- 七、一套标准流程,以后直接复制用
- 八、总结:跨平台编译的核心哲学
Bilibili 同步视频
在跨平台 C/C++ 开发中,动态库编译 堪称最经典的 "细节地狱"------ 同样一套代码,Linux 跑通、macOS 正常,一到 Windows 就报链接错误、缺 .lib、符号找不到;加了导出宏,Linux 又报错不认识语法;好不容易兼容,静态库又出幺蛾子......
今天就以 xlog 日志库为例,把Windows 动态库导出、跨平台宏兼容、CMake 自动处理、编码规范全链路讲透,一次搞定三端编译 ✅
一、先踩第一个坑:Windows 动态库「只出 DLL,不出 LIB」
我们先用 CMake 常规构建动态库:
bash
cmake -S . -B build
cmake --build build
结果在 Windows 上出现致命问题:
-
✅ 生成了
xlog.dll(真正的二进制实现) -
❌ 缺失 ****
xlog.lib(链接器需要的符号索引)
链接器直接报错:无法找到 xlog.lib,项目编译中断。
🔍 核心原理:Windows 与类 Unix 系统的本质差异
| 平台 | 动态库构成 | 符号机制 |
|---|---|---|
| Windows | .dll + .lib |
必须显式 __declspec(dllexport) 才生成 .lib 索引文件 |
| Linux/macOS | .so/.dylib |
默认全局符号可见,无需额外导出声明 |
简单说:
-
Windows :
.dll存代码,.lib存地址索引,缺一不可; -
Linux/macOS:一个文件搞定,符号默认全暴露。
这就是跨平台动态库最核心的分歧点 ⚠️
二、修复第一步:给头文件加上「导出声明」
要让 Windows 生成 .lib,必须在库自身编译时标记符号导出:
cpp
// xlog.h 最初版本(仅 Windows 可用)
#pragma once
// 导出 xlog 内函数/类,生成 .lib 索引
__declspec(dllexport) void XLogPrint(const char* msg);
重新编译:
bash
rm -rf build # 必须清空旧构建!
cmake -S . -B build
cmake --build build
✅ 成功生成:
-
xlog.dll -
xlog.lib
测试程序 test_xlog 也能正常链接运行。
三、第二个坑:调用方「被误导出」,生成多余 LIB
上面的写法有个隐形 BUG:
test_xlog 作为调用方 ,只是引用 xlog 库,结果也被标记导出,生成了无用的 test_xlog.lib。
正确逻辑:导出 / 导入必须分离
-
编译
xlog库自身 → 导出(dllexport) -
编译
test_xlog调用方 → 导入(dllimport)
直接写死肯定不行,因为同一个头文件会被两方包含。
解决方案:用宏自动区分
cpp
// xlog.h 优化版
#pragma once
// 库项目编译:导出
#ifdef XLOG_EXPORTS
# define XCPP_API __declspec(dllexport)
// 调用方使用:导入
#else
# define XCPP_API __declspec(dllimport)
#endif
// 用宏包裹接口
XCPP_API void XLogPrint(const char* msg);
CMake 神助攻:自动生成 XLOG_EXPORTS
CMake 有个隐藏特性:
当你用 add_library(xlog SHARED ...) 时,会自动定义 xlog_EXPORTS 宏,完全不用手动传参!
这就是构建系统帮我们做的「优雅封装」✨
四、第三个坑:Linux/macOS 不认识 Windows 语法
加上导出 / 导入宏后,Windows 完美跑通,但切到 Linux 直接报错:
error: unknown type name '__declspec'
原因很简单:
__declspec** 是 Windows 独占语法,GCC/Clang 完全不识别**。
终极跨平台兼容宏(三端通吃)
cpp
// xlog.h 最终跨平台版
#pragma once
// 1. 只在 Windows 处理导出/导入
#ifdef _WIN32
# ifdef XLOG_EXPORTS
# define XCPP_API __declspec(dllexport)
# else
# define XCPP_API __declspec(dllimport)
# endif
// 2. Linux/macOS 直接空定义,兼容语法
#else
# define XCPP_API
#endif
// 接口声明
XCPP_API void XLogPrint(const char* msg);
CMake 配合平台判断,彻底稳了
cmake
# CMakeLists.txt 平台隔离
if(NOT DEFINED _WIN32)
# Linux/macOS:导出宏为空
add_definitions(-DXCPP_API=)
endif()
现在三端编译状态:
-
✅ Windows:正常生成
.dll+.lib,链接无误 -
✅ Linux:编译
.so,符号可见 -
✅ macOS:编译
.dylib,Xcode 构建正常
五、必看细节:编码格式导致的诡异报错
跨平台开发还有个隐形杀手 :文件编码不一致。
-
Windows VS 默认:GBK
-
Linux/macOS 默认:UTF-8
表现:代码逻辑完全正确,却报语法错误、字符乱码、编译失败。
强制规范:统一用 UTF-8 with BOM
在编辑器中执行:
-
转为
UTF-8编码 -
保存为 UTF-8 with BOM(带签名)
跨平台项目,源码编码必须统一,这是比写代码更基础的底线 📌
六、静态库怎么办?一行宏搞定兼容
动态库搞定了,静态库也要兼容:
静态库不需要导出 / 导入声明。
最终增强宏:
cpp
// 支持 静态库 / 动态库 双模式
#ifdef XLOG_STATIC
# define XCPP_API
#else
# ifdef _WIN32
# ifdef XLOG_EXPORTS
# define XCPP_API __declspec(dllexport)
# else
# define XCPP_API __declspec(dllimport)
# endif
# else
# define XCPP_API
# endif
#endif
七、一套标准流程,以后直接复制用
以后做跨平台动态库,按这个步骤走,零踩坑:
-
清空构建目录 :
rm -rf build(换平台 / 改配置必做) -
统一编码:全部 UTF-8 with BOM
-
头文件加跨平台导出宏(直接复制上面模板)
-
CMake 自动处理 ****
_EXPORTS,不手写宏传递 -
平台隔离 :
_WIN32区分 Windows,其他系统空定义 -
测试三端:Windows → Linux → macOS 依次验证
八、总结:跨平台编译的核心哲学
跨平台不是「把代码跑通」,而是把差异藏起来:
-
用宏封装平台特异语法
-
用CMake自动处理构建差异
-
用统一编码规避隐形故障
-
用导出 / 导入分离保证接口干净

做到这四点,你的库就能真正实现:
「一次编写,Windows/Linux/macOS 三端无缝运行」 🚀