我有一个使用makefile编译的项目。我想在编译时将git diff的输出保存到可执行文件中。简单地将git diff的输出赋值给makefile中的一个变量,然后传递给编译器,例如
GIT_DIFF := $(shell git diff)
CXXFLAGS += -D$(GIT_DIFF)失败的原因是字符串的某些部分被求值,例如,换行符将编译命令拆分为几个无意义的命令。
在编译时将git diff的输出传递为可以在C++代码中使用的字符串的最佳方法是什么?
发布于 2021-01-27 10:00:05
我找到了一个方法来做到这一点。不过,我不太确定它的健壮性和可移植性。这个解决方案假设shell是bash。在makefile中我有
GIT_DIFF := $(shell printf '%q\n' "$$(git diff | awk '{ gsub("\"","\\\"",$$$$1) ; print $$$$1 }' ORS='\\n')" )
CXXFLAGS += -DGIT_DIFF=\"$(GIT_DIFF)\"然后在头文件中,我有
// Convert macro to a string constant
#define STRINGIFY1(x) #x
#define STRINGIFY(x) STRINGIFY1(x)
constexpr auto git_diff = STRINGIFY(GIT_DIFF)(这是一个C++11项目)。
稍微解压一下该命令:
$$转义makefile中的$符号,以便bash看到命令printf '%q\n‘"$(git diff | awk '{ gsub(“\”,“\\”,$$1);print $$1 }’ORS='\n')“(我在这里了解到了这一点,对于awk命令,gsub()位转义引号,因为否则C++编译器(可能也是bash)会被混淆。(从这里开始,https://www.cyberciti.biz/faq/awk-find-and-replace-fields-values/)
ORS='\\n' bit将换行符替换为\\n,这样它们就不会被printf解释为行尾(这篇博客文章的功劳在于https://www.cyberciti.biz/faq/awk-find-and-replace-fields-values/)
printf来转义所有特殊字符(据我所知,这是可移植性较差的代码,如果您想使用不同的shell,需要检查一下)。(从此处复制https://unix.stackexchange.com/a/141323)传递给C++代码的字符串充满了转义序列,因此可读性不是很好。通过颠倒awk替换并使用echo -e计算转义字符,可以将其转换回更具可读性的内容(出于某种原因,这需要执行两次)。因此,如果字符串被保存到文本文件中,比如mygitdiff,那么
echo -e $(echo -e "$(cat mygitdiff | awk '{ gsub("\\\"", "\"",$$1) ; print $$1 }' IRS='\\n')")应该让它具有可读性。
这种方法很笨拙,传递给C++的字符串可读性也不是很好,所以如果有人有更好的解决方案或改进建议,那将是非常受欢迎的!
发布于 2021-01-27 20:22:56
另一种可能是使用从makefile调用的Python脚本将git diff写入到头文件中。我提出的解决方案有一些错误处理。它是C++11特定的,因为它使用“原始字符串文字”来避免需要转义特殊字符(也使用constexpr auto,但可以用const std::string替换)。这个函数保存了一个来自git describe的散列以及diff。
Python脚本:
#!/usr/bin/env python3
from pathlib import Path
from string import ascii_uppercase
try:
from git import Repo
except ImportError:
# print a return code to stdout so we can capture and test it in the makefile
print(11)
raise
scriptdir = Path(__file__).parent
repo = Repo(scriptdir.parent)
diff = repo.git.diff()
# Find a string to use for the delimiter
delimiter = "_DIFF_DELIMITER_"
for letter in ascii_uppercase:
if ")" + delimiter not in diff:
delimiter_success = True
break
delimiter = "_DIFF_DELIMITER_" + letter + "_"
if ")" + delimiter in diff:
# print a return code to stdout so we can capture and test it in the makefile
print(12)
raise ValueError(
"save_git_version.py failed to find a delimiter that is not in the git diff"
)
with open("my_git_version.hxx", "w") as f:
f.write("constexpr auto my_git_hash = \"")
f.write(repo.git.describe(abbrev=40, dirty=True, always=True, tags=True))
f.write("\";\n")
f.write("constexpr auto my_git_diff = R\"" + delimiter + "(")
f.write(diff)
f.write(")" + delimiter + "\";\n")
# print a return code to stdout so we can capture and test it in the makefile
print(0)在makefile中:
save_git_version_status := $(shell ./save_git_version.py)
ifeq ($(save_git_version_status), 11)
$(error "gitpython not installed. You can install with 'conda install gitpython' or 'pip install --user gitpython'")
endif
ifeq ($(save_git_version_status), 12)
$(error "save_git_version.py failed to find a delimiter that is not in the git diff")
endif
ifneq ($(save_git_version_status), 0)
$(error "save_git_version.py failed with an unrecognised error $(save_git_version_status)")
endif然后,您可以#include "my_git_version.hxx" in the C++ file where you want the my_git_hashormy_git_diff`字符串。
https://stackoverflow.com/questions/65911886
复制相似问题