在Go中生成构建时间戳记

时间:2018-10-28 11:33:07

标签: go

我希望在Go程序中(在Debian / Linux / x86-64上使用Go 1.11.1)保持构建时间戳,并在一行中说明最后一个git commit

在C程序中(我的bismon项目正在做非常相似的事情),我只会生成一些_timestamp.c文件,例如Makefile的食谱,例如:

_timestamp.c: 
    date +'const char my_timestamp[]='%c';%n' > $@
    (echo -n 'const char my_lastgitcommit[]="'; \
     git log --format=oneline --abbrev=12 --abbrev-commit -q | head -1 \
       | tr -d '\n\r\f\"\\\\' ; echo '";') >> $@

,然后将程序myprog与以下内容链接:

myprog: $(MYOBJECTS) _timestamp.c
    $(LINK.c) $(MYOBJECTS) _timestamp.c -o $@
    $(RM) _timestamp.c

请注意,_timestamp.c会在每个成功链接时自动删除。当然,在某些标头中,我会声明extern const char my_timestamp[];extern const char my_lastgitcommit[]:并使用例如{{1}中的my_timestampmy_lastgitcommit(并且main.c包含MYOBJECTS

看起来go generate可以以类似的方式使用。我想使用一个软件包main.o来定义两个字符串全局变量"timestamp"timestamp.My_timestamp,但我不完全了解该怎么做。

我尝试添加一些timestamp.My_gitcommit文件

timestamp/timestamp.go

但是它并没有随着package timestamp //go:generate date +'var My_timestamp = "%c"%n' // Code generated - DO NOT EDIT. 然后go generate改变

当然,这些时间戳在编译时应该是常量字符串,我希望在ELF可执行文件上运行strings(1)实用程序时会找到它们。

顺便说一句,我记得the go command的动机之一:

  

从一开始,Go的一个明确目标就是能够仅使用在源代码本身中找到的信息来构建Go代码,而无需编写makefile或许多现代的makefile替代之一。如果Go需要一个配置文件来说明如何构建程序,那么Go将会失败。

因此,我仍然希望单独将一些内容放到源代码中,而无需进行额外的构建。

换句话说,我想在每次构建时都生成一个类似于以下内容的Go文件:

go install

// generated timestamp.go file package timestamp var Buildtime = "Tue 30 Oct 2018 09:39:01 AM MET"; var Buildlastgitcommit = "7fde394b60bc adding timestamp.go" 字符串由Buildtime生成。 date +%c字符串可能由类似于我的Buildlastgitcommit make规则执行的命令生成。

我需要这些字符串是恒定的,并内置在Go构建中生成的ELF可执行文件中(我更希望通过通常命令来完成,或者不使用{{1} }或任何其他构建自动化工具,或者如果忘记了必需参数,则通过某种方式使构建失败;因此atanayel's answer是不够的)。因此,我希望strings(1)实用程序能够在可执行文件中快速找到这些字符串。并且此类文件的生成应以某种类型的文件进行配置,而不需要为构建器添加额外的参数。

我可以考虑切换到其他的 Go-friendly build automation,我也无法轻松完成自己想要的工作:每次构建时都会快速生成一些简单的_timestamp.c文件)。但是我不明白为什么在Go程序中使用生成的 Go文件如此困难。 gb之后便是生成简单的代码,并且已经有数十年的实践了(例如,受旧的Unix philosophy程序启发,参见goyacc)。

NB:yacc明确提到:

  

构建像Unix go build实用程序这样的通用构建系统不是本提案的目标。

及以后的

  

问题解决后,作者将生成的文件提交到源存储库中,

(这不是我的用例)

PS。我只关心POSIX系统;我真的不在乎我的Go软件是否不能在Windows上构建。而且我倾向于认为(与rationale for go generate的解释相反),在我的特定情况下,我要做需要一些构建自动化工具。在我的go command motivation玩具项目(GPLv3 +)中,我正在使用.go(驱动make(1)命令)

3 个答案:

答案 0 :(得分:6)

您可以使用构建时间标志来执行此操作。 This answer针对不同的用例进行了解释,但您的使用情况与之相同。 你应该做这样的事情。

  • 在代码中定义一个常量,例如buildTime
  • 在构建发行版时,请执行类似go build -ldflags='-X buildTime="My build time output from a command"'
  • 的操作
  • 现在您的变量buildTime是常量,等于My build time output from a command
  • 您可以使用上一个命令找到正确的My build time output froma a command

请参阅我链接的答案以获得更好的解释。

答案 1 :(得分:3)

正如提到的其他答案一样,您可以在构建时将所需的值注入二进制文件。

例如,如果我们具有以下程序包变量:

// build flags
var (
    BuildTime  string
    CommitHash string
    GoVersion  string
    GitTag     string
)

然后我们可以使用以下bash脚本填充它们:

#!/bin/sh

clear

TRG_PKG='main'
BUILD_TIME=$(date +"%Y%m%d.%H%M%S")
CommitHash=N/A
GoVersion=N/A
GitTag=N/A

if [[ $(go version) =~ [0-9]+\.[0-9]+\.[0-9]+ ]];
then
    GoVersion=${BASH_REMATCH[0]}
fi

GV=$(git tag || echo 'N/A')
if [[ $GV =~ [^[:space:]]+ ]];
then
    GitTag=${BASH_REMATCH[0]}
fi

GH=$(git log -1 --pretty=format:%h || echo 'N/A')
if [[ GH =~ 'fatal' ]];
then
    CommitHash=N/A
else
    CommitHash=$GH
fi

FLAG="-X $TRG_PKG.BuildTime=$BUILD_TIME"
FLAG="$FLAG -X $TRG_PKG.CommitHash=$CommitHash"
FLAG="$FLAG -X $TRG_PKG.GoVersion=$GoVersion"
FLAG="$FLAG -X $TRG_PKG.GitTag=$GitTag"

if [[ $1 =~ '-i' ]];
then
    echo 'go install'
    go install -v -ldflags "$FLAG"
else
    echo 'go build'
    go build -v -ldflags "$FLAG"
fi

示例代码来自此repo

答案 2 :(得分:0)

export LAST_BUILD=$(date +"%T") ; go build ...

Os.Getenv("LAST_BUILD")