fmt
用 CMake 集成 fmt:编译库、仅头文件与模块构建
把 fmt 部署理解为依赖与 ABI 决策:固定源码、明确选择 CMake 目标,并区分局部头文件夹具与生产构建矩阵。
你将学会
- 部署的是依赖,不是虚构的服务
- 显式表达构建选择
- 验证最终真正交付的产物
开始前需要
- 理解基础 C++ 值、引用、字符串与构建目标
- 能够区分文档预期与实际测量结果
追踪具体 fmt 行为,在输出、生命周期和验证边界明确的前提下选择集成方式。
先看结论
- 明确选择 CMake 目标并固定依赖修订。
- C++11 基线不代表所有编译期或模块功能都可用。
- 局部仅头文件夹具不能证明安装包、共享库或模块部署。
部署的是依赖,不是虚构的服务
真正需要交付的是应用及其选择的 fmt 集成方式。官方入门文档给出了编译库目标 fmt::fmt、仅头文件目标 fmt::fmt-header-only,以及可选模块目标。文档出于构建时间考虑推荐编译库或模块方式,但这不等于已经测出你的应用能够获得多少加速。
为了可复现,应内置或获取精确修订,并记录使用它的工具链与配置。所查普通 CMake 目标声明 C++11 基线,而教学夹具刻意使用 C++20,以覆盖字面量 consteval 检查。库的最低支持语言级别,并不意味着每一种可选功能都能在同一级别工作。
显式表达构建选择
下面的 CMake 片段假定固定仓库已放在 third_party/fmt。为了简化消费端示例,它关闭文档、上游测试、安装目标和模块集成,应用直接链接 fmt::fmt,而不是手工拼接包含目录后期待链接器找到正确二进制。这是参考集成方式,不代表本机已经运行完整的 CMake 构建。
仅头文件目标会传播 FMT_HEADER_ONLY 定义,并通过头文件引入实现代码。这样不需要单独的 fmt 库文件,但可能增加重复编译工作。原生模块路径还依赖生成器与编译器检查;所查 CMake 的 GNU 条件要求 GCC 15,尽管附近较早的注释仍提到 GCC 14。应以实际执行的条件为依据。
验证最终真正交付的产物
BUILD_SHARED_LIBS 会影响库的形式;在相关平台上,把静态库嵌入另一个共享对象还可能需要位置无关代码。生产者与消费者之间应保持编译器 ABI、运行库链接、构建模式和依赖版本一致。头文件能够成功包含,并不能覆盖所有链接错误或部署后的动态库不匹配。
有界编辑夹具使用的六个源码头文件,已经逐字节比对固定 Git 树中的对象哈希。这不能替代对 CMake 生成器、已安装软件包、共享库加载器、C++ 模块路径和目标平台的测试。发布前应按最终交付的集成模式运行应用测试,并记录对应产物身份。
实施步骤
- 1
把经过核验的修订放入 third_party/fmt。
- 2
明确选择编译库、仅头文件或模块方式。
- 3
使用目标编译器和运行库链接配置构建并测试应用。
- 4
检查部署产物,不只检查头文件是否能找到。
可复制示例
cmake_minimum_required(VERSION 3.28)
project(fmt_demo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(FMT_DOC OFF CACHE BOOL "" FORCE)
set(FMT_TEST OFF CACHE BOOL "" FORCE)
set(FMT_INSTALL OFF CACHE BOOL "" FORCE)
set(FMT_MODULE OFF CACHE BOOL "" FORCE)
add_subdirectory(third_party/fmt EXCLUDE_FROM_ALL)
add_executable(fmt_demo main.cc)
target_link_libraries(fmt_demo PRIVATE fmt::fmt)常见问题
仅头文件方式一定构建最快吗?
不是。它使用方便,但重复编译是另一项成本。上游建议用编译库或模块改善构建时间,实际项目仍需测量。
本次编辑检查验证了 CMake 模块和生产 ABI 吗?
没有。这需要单独的编译器、生成器和应用部署矩阵,局部仅头文件夹具无法证明这些路径。
资料来源
- CMakeLists.txt来源核查 2026-09-08
- doc/get-started.md来源核查 2026-09-08
- include/fmt/core.h来源核查 2026-09-08
- include/fmt/format.h来源核查 2026-09-08