c++ - 如何使 Git 提交哈希在 C++ 代码中可用而无需重新编译?
问题描述
一个相当普遍的要求,我想:我想myapp --version
显示版本和 Git 提交哈希(包括存储库是否脏)。该应用程序是通过 a 构建的Makefile
(实际上是由 生成的qmake
,但现在让我们保持它“简单”)。我相当精通Makefiles,但是这个让我很难过。
我可以像这样轻松获得所需的输出:
$ git describe --always --dirty --match 'NOT A TAG'
e0e8556-dirty
C++ 代码期望提交哈希可用作名为 的预处理器宏GIT_COMMIT
,例如:
#define GIT_COMMIT "e0e8556-dirty" // in an include file
-DGIT_COMMIT=e0e8556-dirty // on the g++ command line
以下是我尝试将git describe
输出连接到 C++ 的几种不同方法。它们都不能完美地工作。
方法一:$(shell)
功能。
我们使用 make 的$(shell)
函数来运行 shell 命令并将结果粘贴到 make 变量中:
GIT_COMMIT := $(shell git describe --always --dirty --match 'NOT A TAG')
main.o: main.cpp
g++ -c -DGIT_COMMIT=$(GIT_COMMIT) -o$@ $<
这适用于干净的构建,但有一个问题:如果我更改 Git 哈希(例如,通过提交或修改干净工作副本中的某些文件),make 看不到这些更改,并且二进制文件不会被重建。
方法二:生成version.h
在这里,我们使用一个 make recipe 来生成一个version.h
包含必要的预处理器定义的文件。目标是虚假的,因此它总是会被重建(否则,在第一次构建后它总是被视为最新的)。
.PHONY: version.h
version.h:
echo "#define GIT_COMMIT \"$(git describe --always --dirty --match 'NOT A TAG')\"" > $@
main.o: main.cpp version.h
g++ -c -o$@ $<
这可以可靠地工作并且不会错过对 Git 提交哈希的任何更改,但这里的问题是它总是重建version.h
以及依赖它的所有内容(包括相当长的链接阶段)。
方法三:仅version.h
在发生变化时生成
这个想法:如果我将输出写入version.h.tmp
,然后将其与现有的进行比较,version.h
并且仅在它不同时覆盖后者,我们并不总是需要重建。
但是,在实际开始运行任何配方之前,请弄清楚它需要重建什么。所以这必须在那个阶段之前,即也从一个$(shell)
函数运行。
这是我的尝试:
$(shell echo "#define GIT_COMMIT \"$$(git describe --always --dirty --match 'NOT A TAG')\"" > version.h.tmp; if diff -q version.h.tmp version.h >/dev/null 2>&1; then rm version.h.tmp; else mv version.h.tmp version.h; fi)
main.o: main.cpp version.h
g++ -c -o$@ $<
这几乎可行:每当 Git 哈希更改时,第一个构建会重新生成version.h
并重新编译,但第二个构建也是如此。从那时起,make 决定一切都是最新的。
因此,似乎 make 甚至在它运行$(shell)
函数之前就决定了要重建的内容,这也使得这种方法也被破坏了。
这似乎是一件很常见的事情,并且由于 make 是一个如此灵活的工具,我很难相信没有办法让这 100% 正确。这种方法存在吗?
解决方案
我在这里找到了一个不错的解决方案:
在你的CMakeLists.txt
地方:
# Get the current working branch
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE)
# Get the latest commit hash
execute_process(
COMMAND git rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE)
然后在您的源代码中定义它:
target_compile_definitions(${PROJECT_NAME} PRIVATE
"-DGIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\"")
在源代码中,它现在可以作为#define
. 人们可能想通过包括以下内容来确保源代码仍然正确编译:
#ifndef GIT_COMMIT_HASH
#define GIT_COMMIT_HASH "?"
#endif
然后你就可以使用了,例如:
std::string hash = GIT_COMMIT_HASH;
推荐阅读
- flutter - 扩展和显示 firebase 用户个人资料信息始终为空
- primefaces - p:selectOneListbox 滚动到预选项目
- c# - 实体框架核心 2.1:无法翻译,将在本地评估
- sas-visual-analytics - 修改日期变量
- angular - 使用动态离子段触发两次 ionChange 事件
- javascript - EyeBall 不会跟随 onMouseMove 事件
- css - 将多个元素放在 CSS 网格的单个区域中
- google-apps-script - 当单元格值从公式更改时发送电子邮件
- python - 在两个级别输入有效值之前,如何继续要求输入?
- clojure - 如何使用 core.logic 在地图数据库中搜索有效的嵌套地图?