在Go中生成构建时间戳

Bas*_*tch 9 go

我想要一个Go程序(在Debian/Linux/x86-64上使用Go 1.11.1)来保持构建时间戳,并用一行解释最后一行git commit.

在一个C程序(FWIW我的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 '";') >> $@
Run Code Online (Sandbox Code Playgroud)

我会把我的程序myprog与以下内容联系起来:

myprog: $(MYOBJECTS) _timestamp.c
    $(LINK.c) $(MYOBJECTS) _timestamp.c -o $@
    $(RM) _timestamp.c
Run Code Online (Sandbox Code Playgroud)

请注意,_timestamp.c每个成功链接都会自动删除.当然在某些标题中我会声明extern const char my_timestamp[];并且extern const char my_lastgitcommit[]:我会使用eg my_timestampmy_lastgitcommitin main.c(并且MYOBJECTS包含main.o)

它似乎go generate可以用来以类似的方式表现.我想有一个包"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.
Run Code Online (Sandbox Code Playgroud)

但它并没有随之变化go generate,然后go install

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

顺便说一下,我记得这个go命令的一个动机:

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

所以我仍然希望单独进入源代码,而不需要额外的构建配置.

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

// generated timestamp.go file
package timestamp
var Buildtime = "Tue 30 Oct 2018 09:39:01 AM MET";
var Buildlastgitcommit = "7fde394b60bc adding timestamp.go"
Run Code Online (Sandbox Code Playgroud)

Buildtime字符串由date +%c.生成.该Buildlastgitcommit字符串可能由类似于我的_timestamp.cmake规则所做的命令生成.

我需要这些字符串是常量的并且内置由Go构建生成的ELF可执行文件(我更喜欢通过常用命令完成,或者没有额外的参数go build或任何其他构建自动化工具,或者某种方式使其失败如果忘记了强制性的论点,那就建立起来;因此atanayel的答案是不够的.所以我希望strings(1)实用程序在可执行文件中快速找到这些字符串.并且应该在某种文件中配置此类文件的生成,而不需要额外的构建器参数.

我可以考虑改用其他一些,去友好,构建自动化系统(但似乎即使有GB我不能轻易做什么,我想:快速生成一些简单的.go在每个构建文件).但我不明白为什么在Go程序中使用生成的 Go文件如此困难.生成简单的代码遵循Unix哲学,并且已经实践了几十年(例如,见旧的yacc程序启发的goyacc).

注意:明确提到的理由go generate是:

构建像Unix make(1)实用程序这样的通用构建系统并不是本提议的目标.

然后

一旦确定了问题,作者就会将生成的文件提交到源存储库,

(这不是我的用例)

PS.我只关心POSIX系统; 我真的不在乎我的Go软件是否无法在Windows上构建.而且我倾向于认为(与go命令动机的解释相反),在我的特定情况下,我确实需要一些构建自动化工具.在我的bastawigo玩具项目(GPLv3 +)中,我正在使用make(驾驶go命令)

ata*_*nel 7

您可以使用构建时标志来执行此操作.这个答案解释了它用于不同的用例,但是你的用例与之相同.你应该做这样的事情.

  • 在代码中定义一个常量 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.

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


Kav*_*ian 5

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

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

// build flags
var (
    BuildTime  string
    CommitHash string
    GoVersion  string
    GitTag     string
)
Run Code Online (Sandbox Code Playgroud)

然后,我们可以使用以下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
Run Code Online (Sandbox Code Playgroud)

示例代码来自此仓库