【问题标题】:Include Git commit hash and/or branch name in C/C++ source在 C/C++ 源代码中包含 Git 提交哈希和/或分支名称
【发布时间】:2017-10-17 17:13:11
【问题描述】:

我想知道如何在编译后的二进制文件中将 Git 提交哈希和/或其他信息放入 C++ 变量的内容中,而无需将其作为 Git 跟踪的源的一部分。

我需要在嵌入式处理器上运行的已编译可执行文件中跟踪固件版本信息。识别固件二进制文件特定版本的方法,例如有意义的文件名、MD5 校验和甚至日期/时间戳在这个封闭的环境中不可用(即没有文件系统)。

一种方法是让设备的控制台输出生成识别文本,例如“Release 1.2.3”、“commit hash 1bc123...”或类似内容。固件版本信息仅对维护人员感兴趣,因此经过培训的操作员可以检查控制台输出。要实现这一点,可能需要手动编辑版本字符串,然后将其编译成代码并在程序启动时输出到控制台。

这对于使用签核工作流程来仔细检查版本信息是否正确的主要版本是可以接受的。然而,这是一个手动过程,它本质上是不可靠的。比如开发者忘记更新版本信息怎么办? - 现在已编译的代码与其报告的版本字符串之间存在脱节。

每次用户想要测试硬件时都重新编译和下载代码的工作流程在所讨论的情况下是不切实际的,即更新固件非常繁重。

因此需要一种自动识别代码版本的方法。在有问题的情况下,使用了 Git,开发人员定期将他们的工作提交给功能分支。显示 Git 提交哈希,以及是否有未暂存的更改,将是识别用于编译固件的源代码状态的一种方式。

要求是我希望我的应用程序有可用的信息,以便它能够显示: "Git 提交:[01abcdef...etc],分支:experimentalStuffDoNotRelease"

因此,我想在编译的 C 和/或 C++ 代码中自动包含 Git 信息,例如提交哈希和分支。

开发环境有使用 Windows 和 Linux 的开发人员,并使用 Eclipse CDT 和相对简单的工作流程:检查;编译;下载到硬件。

【问题讨论】:

  • 如果您将其归结为基本要素,这将是一个更好的问题:省略所有工作流程的内容,只需询问如何将 git 提交哈希和/或其他信息放入内容中编译后的二进制文件中的 C++ 变量,而不是它是 git 跟踪的源的一部分。 (知道好的答案看起来应该会让这更容易)。另请参阅meta.stackoverflow.com/questions/351446/lifting-the-review-ban
  • @PeterCordes,谢谢 - 我实际上添加了更多信息,因为我有一个“太宽泛”的诅咒。另外,我特别想针对 Eclipse,因为它没有使用标准的“制作”工作流程 (AFAIK)。
  • 您应该编辑它,以便尽早明确真正的问题。在说明具体问题后,您可以稍后详细说明您想要它的原因。询问如何为 Eclipse 构建执行此操作对我来说似乎非常具体。所有关于工作流程的讨论都会分散注意力,并为可能的替代解决方案打开了大门,这可能是它过于宽泛的原因。您的用例只是让程序能够打印其版本信息(包括提交 ID)很方便的众多原因之一。
  • 哦,刚刚看了你的第一个版本。与其询问如何让您的应用程序显示它,不如询问如何在构建时将 git 版本信息获取到变量中,而无需将版本字符串作为版本控制源的一部分。问怎么展示肯定是太笼统了;在将版本字符串放入 CPP 宏或全局变量后,我们将把这部分留给您。

标签: c++ c git eclipse-cdt


【解决方案1】:

我使用 makefile,如下所示:

GIT_HASH=`git rev-parse HEAD`
COMPILE_TIME=`date -u +'%Y-%m-%d %H:%M:%S UTC'`
GIT_BRANCH=`git branch | grep "^\*" | sed 's/^..//'`
export VERSION_FLAGS=-DGIT_HASH="\"$(GIT_HASH)\"" -DCOMPILE_TIME="\"$(COMPILE_TIME)\"" -DGIT_BRANCH="\"$(GIT_BRANCH)\""

all:
    g++ main.cpp $(VERSION_FLAGS)

当 makefile 运行时,git hash 和编译时间都被加载到源中可访问的宏中,如下所示:

#include <iostream>

int main(){
  std::cerr<<"hash="<<GIT_HASH<<", time="<<COMPILE_TIME<<", branch="<<GIT_BRANCH<<std::endl; 
}

输出如下:

hash=35f531bf1c959626e1b95f2d3e1a7d1e4c58e5ec, time=2017-05-18 04:17:25 UTC, branch=master

【讨论】:

  • 一般不需要同时调用grepsed就可以得到分支,(例如GIT_BRANCH=$(git branch | sed -n 's/^[*][ ]//p')
  • 这些宏也适用于带有 fpp 预处理器(在 ifort 或 gfortran 中)的 Fortran。
  • 你也可以得到分支名称GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
【解决方案2】:

通常作为构建的一部分,您会运行一些生成类似内容的命令。 例如,git describe 为您提供了一些您可以使用的东西:

echo // auto generated version: > version.h
git describe > echo // auto generated version: > version.h
echo -e "#define VERSION " >> version.h
git describe >> version.h

例如 x264 使用this simple script 来生成它:

if [ -d .git ] && command -v git >/dev/null 2>&1 ; then
    localver="$(($(git rev-list HEAD | wc -l)))"
    if [ "$localver" -gt 1 ] ; then
        ver_diff="$(($(git rev-list origin/master..HEAD | wc -l)))"
        ver="$((localver-ver_diff))"
        echo "#define X264_REV $ver"
        echo "#define X264_REV_DIFF $ver_diff"
        if [ "$ver_diff" -ne 0 ] ; then
            ver="$ver+$ver_diff"
        fi
        if git status | grep -q "modified:" ; then
            ver="${ver}M"
        fi
        ver="$ver $(git rev-list -n 1 HEAD | cut -c 1-7)"
        version=" r$ver"
    fi
fi

echo "#define X264_VERSION \"$version\""
echo "#define X264_POINTVER \"0.$ver\""

这将产生类似的东西:

#define X264_VERSION  " r2708 86b7198"
#define X264_POINTVER "0.148.2708 86b7198"

【讨论】:

  • 该脚本中的几个命令可以大大简化,例如,ver_diff=$(git rev-list --count origin/master..HEAD)ver=$(git rev-parse --short=7 HEAD)
【解决方案3】:

在 Eclipse CDT 中,使用预构建步骤生成包含相关信息的包含文件,并使用条件包含来检查文件是否已创建:

  1. 右击项目

  2. 选择属性

  3. 扩展 C/C++ 构建

  4. 选择构建步骤中的设置选项卡

  5. 在命令文本框中输入以下内容:

      git log --pretty=format:'#define GIT_INFO_PRESENT%n static const char* GIT_INFO = "Version Information=[%H,%d]\r\n";' -n 1 > ../src/gitcommit.h
    

    这将在构建时创建一个文件gitcommit.h,该文件将包含在源代码中。要自定义它,请根据您的需要调整字符串。 (见https://git-scm.com/docs/pretty-formats

例如,我在主例程开始时生成一个调试输出,以通知用户提交和分支(不是严格需要知道提交,但肯定有帮助):

  1. 把这个放在文件里,可能在顶部

     #if __has_include("gitcommit.h")
     #include "gitcommit.h"
     #else
     static const char* GIT_INFO = "Git version information not present.\r\n";
     #endif
    
  2. 要在代码中的某处显示信息,请执行以下类似操作:

     printf(GIT_INFO);
    

请注意,在这种情况下,我没有将预构建步骤设为 shell 脚本或 Windows/DOS .bat 文件,因为我经常在 Linux 或 Windows 中工作。

请注意,这未在 Windows 中进行测试。

在这两种情况下,'git' 必须可以从标准命令行执行。

依赖于__has_include 的提供。这旨在提供简单性,以便不需要提供默认包含文件。

请注意,gitcommit.h 文件的路径应该可以被编译器发现。

【讨论】:

  • 这是一个很好的方法,但是我使用的是 windows 并且 eclipse 一直抛出它找不到 git 命令的错误。 PATH 已更新,并且 git 可以在任何 cmd 中运行。知道是什么问题吗?
【解决方案4】:

如果您只需要 git hash 和本地修改标志,这里有一个简单的解决方案:

$ cat update-version-info.sh

#!/bin/sh
version=$(git describe --always --dirty --tags)
echo "#define GIT_VERSION \"$version\"" > git-version.h

$ cat 1.c

#include <stdlib.h>
#include <stdio.h>

#include "git-version.h"

int main() {
#ifdef GIT_VERSION
    printf("%s\n", GIT_VERSION);
#endif
    return 0;
}

$ ./1

ee4f307-dirty

【讨论】:

    【解决方案5】:

    AC 文件或 C++ 文件可由某些程序生成(例如,Linux 上的某些 shell 脚本,或某些 GNU awk 脚本,或运行 system 或 @987654323 的 C 程序@ 运行一些git 命令)。

    您只需适当地配置您的build automation 工具(例如,如果您使用GNU make,则为您的Makefile,如果您使用build.ninja,则为build.ninja 文件@)。

    BismonRefPerSys 都在这样做,并将它们的 git id 保存在获得的可执行文件中(在使用 --version 调用时显示它)。注意:两个都是我创建的项目。

    PS 另一个问题是如何配置您的 IDE 以运行特定的构建自动化工具。这是特定于您的IDE。但是Eclipse CDT FAQ 提供了一个见解。

    【讨论】:

      【解决方案6】:

      基于@Richard 的回答和我对 qmake/Qt 的修复,这是我使用的解决方案: 对于 Qt 5.14,.pro 文件中的以下行对我有用:

      GIT_HASH="\\\"$$system(git -C \""$$_PRO_FILE_PWD_"\" rev-parse --short HEAD)\\\""
      GIT_BRANCH="\\\"$$system(git -C \""$$_PRO_FILE_PWD_"\" rev-parse --abbrev-ref HEAD)\\\""
      BUILD_TIMESTAMP="\\\"$$system(date -u +\""%Y-%m-%dT%H:%M:%SUTC\"")\\\""
      DEFINES += GIT_HASH=$$GIT_HASH GIT_BRANCH=$$GIT_BRANCH BUILD_TIMESTAMP=$$BUILD_TIMESTAMP
      

      在您的代码中,您可以像这样检查修订:

      int main(int argc, char *argv[])
      {
          QStringList args;
          for (int i = 0; i < argc; i++)
              args << QString(argv[i]);
      
          if (args.contains("-v") || args.contains("--version")) {
              std::cout << QString("branch: %1, version: %2, built_at: %3").arg(GIT_BRANCH).arg(GIT_HASH).arg(BUILD_TIMESTAMP).toUtf8().constData() << std::endl;
              return 0;
          }
          // ...
      }
      

      【讨论】:

        猜你喜欢
        • 2015-06-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-29
        • 2021-12-22
        • 2022-08-02
        • 1970-01-01
        相关资源
        最近更新 更多