硬编码版本号会导致维护困难、易出错且无法准确追溯 git 提交与版本关系;理想做法是用 cmake 的 configure_file() 在 configure 阶段动态生成 version.h,结合 git 信息确保版本真实反映代码状态,并通过 include_directories(${cmake_binary_dir}) 支持 ide 正确识别。

为什么不能直接在代码里写死版本号
硬编码版本号会导致每次发布都要手动改源码,容易出错且无法追溯 Git 提交与版本的对应关系。更糟的是,CI 构建时若没同步更新,VERSION_H 和实际 tag 可能不一致,调试和问题定位会变困难。
理想做法是让 CMake 在 configure 阶段从 Git 信息(如最近 tag、提交距 tag 的偏移、是否 dirty)动态生成头文件,确保每次构建的版本号真实反映代码状态。
用 configure_file() 生成带变量的头文件
CMake 自带的 configure_file() 是最轻量、最可靠的方式。它把模板文件中的 @VAR@ 占位符替换成 CMake 变量值,再输出为实际头文件。
- 先在项目根目录建一个模板文件
version.h.in,内容类似:
#define APP_VERSION_MAJOR @APP_VERSION_MAJOR@ #define APP_VERSION_MINOR @APP_VERSION_MINOR@ #define APP_VERSION_PATCH @APP_VERSION_PATCH@ #define APP_VERSION_COMMIT "@APP_VERSION_COMMIT@" #define APP_VERSION_DIRTY @APP_VERSION_DIRTY@ #define APP_VERSION_IS_TAG @APP_VERSION_IS_TAG@
- 在
CMakeLists.txt中设置变量并调用:
set(APP_VERSION_MAJOR 1)
set(APP_VERSION_MINOR 2)
set(APP_VERSION_PATCH 0)
<h1>获取 Git 信息(需 Git 可执行且工作区干净)</h1><p>find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --always --dirty --abbrev=8
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_DESCRIBE
OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REPLACE "-" ";" GIT_PARTS ${GIT_DESCRIBE})
list(GET GIT_PARTS 0 APP_VERSION_COMMIT)
list(LENGTH GIT_PARTS GIT_PARTS_LEN)
if(GIT_PARTS_LEN GREATER 1)
set(APP_VERSION_DIRTY 1)
else()
set(APP_VERSION_DIRTY 0)
endif()</p><h1>判断是否正好在 tag 上(无 commit 偏移)</h1><p>execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --exact-match HEAD
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE GIT_EXACT_RESULT
OUTPUT_QUIET ERROR_QUIET)
if(GIT_EXACT_RESULT EQUAL 0)
set(APP_VERSION_IS_TAG 1)
else()
set(APP_VERSION_IS_TAG 0)
endif()
endif()</p><p>configure_file(version.h.in ${CMAKE_BINARY_DIR}/version.h @ONLY)</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill6035" title="GitHub Safe Sync"><img
src="https://img.php.cn/upload/skill/000/000/081/179076071969320.jpg" alt="GitHub Safe Sync" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill6035" title="GitHub Safe Sync" class="overflowclass">GitHub Safe Sync</a>
<p class="overflowclass">检查、触发并清理使用安全同步 GitHub Actions 工作流的 GitHub 镜像仓库,用于 Codex 需要进行仓库镜像同步时。</p>
</div>
<a rel="nofollow" href="/xiazai/skill6035" title="GitHub Safe Sync" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 最后记得把生成路径加入 include 目录:
include_directories(${CMAKE_BINARY_DIR}) - 注意:生成的
version.h在构建目录,不是源码目录,避免污染 Git 工作区
避免 add_custom_command() 的常见陷阱
有人倾向用 add_custom_command() + add_custom_target() 手动触发生成,但容易踩坑:
- 生成时机不可控:若 target 依赖未正确定义,
version.h可能在编译前未就绪,导致fatal error: version.h: No such file or directory - 增量构建失效:CMake 不自动感知 Git 状态变化,
git status改了但 CMake 不重运行,头文件不会刷新 - 跨平台命令差异:Windows 下
git路径、换行符、shell 语法都可能出问题,而configure_file()是纯 CMake 实现,无此顾虑
除非你明确需要在 build 时(而非 configure 时)重新提取 Git 信息(比如支持 hot-reload 场景),否则没必要绕开 configure_file()。
如何让版本号在 IDE 中正确跳转和补全
Clion / VS Code + CMake Tools 插件默认只索引源码目录,而 version.h 在 ${CMAKE_BINARY_DIR} 下,IDE 往往找不到定义。
- 确保
include_directories(${CMAKE_BINARY_DIR})已添加,且该路径对所有 target 生效 - Clion 用户:勾选
Settings > Build > CMake > Store paths relative to project root,并确认 CMake profile 使用的是同一build目录 - VS Code 用户:检查
c_cpp_properties.json中includePath是否包含"${workspaceFolder}/build"(或对应路径) - 验证方式:在源码中
#include "version.h"后,按 Ctrl+Click 能跳转到生成的头文件
如果跳转失败,八成是 IDE 没读到 CMake 的 include 设置——别改头文件位置,去查 IDE 的 CMake 集成配置是否生效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










