I want in a Go program (using Go 1.11.1 on Debian/Linux/x86-64) to keep the build timestamp with a line explaining the last git commit
.
In a C program (FWIW my bismon project is doing something very similar), I would just generate some _timestamp.c
file, e.g. with a Makefile
recipe like:
_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 '";') >> $@
and I would link my program myprog
with something like:
myprog: $(MYOBJECTS) _timestamp.c
$(LINK.c) $(MYOBJECTS) _timestamp.c -o $@
$(RM) _timestamp.c
Notice that _timestamp.c
is automatically removed at each successful link. Of course in some header I would declare extern const char my_timestamp[];
and extern const char my_lastgitcommit[]:
and I would use e.g. my_timestamp
and my_lastgitcommit
in my main.c
(and have MYOBJECTS
contain main.o
)
It looks like go generate
could be used to behave in a similar way. I would like to have a package "timestamp"
defining two string globals timestamp.My_timestamp
and timestamp.My_gitcommit
but I don't exactly understand how to do it.
I tried to add some timestamp/timestamp.go
file with
package timestamp
//go:generate date +'var My_timestamp = "%c"%n'
// Code generated - DO NOT EDIT.
But it did not change with go generate
then go install
Of course, these timestamps should be constant strings at compile time, and I expect to find them when running the strings(1) utility on the ELF executable.
BTW, I recall one of the motivations of the go
command:
An explicit goal for Go from the beginning was to be able to build Go code using only the information found in the source itself, not needing to write a makefile or one of the many modern replacements for makefiles. If Go needed a configuration file to explain how to build your program, then Go would have failed.
So I am still expecting something to go into the source code alone, without extra configuration for building.
In other words, I want to generate at every build a Go file similar to:
// generated timestamp.go file
package timestamp
var Buildtime = "Tue 30 Oct 2018 09:39:01 AM MET";
var Buildlastgitcommit = "7fde394b60bc adding timestamp.go"
The Buildtime
string is generated by date +%c
. The Buildlastgitcommit
string might be generated by commands similar to what my _timestamp.c
make rule is doing.
I need these strings to be constant and built-in the ELF executable produced by a Go build (which I would prefer to be done by usual commands, either without extra arguments to go build
or any other build automation tool, or with some way to fail the build if the mandatory arguments are forgotten; hence atanayel's answer is not enough). So I want the strings(1) utility to find these strings quickly in the executable. And the generation of such files should be configured in some kind of files, not requiring extra arguments to builders.
I could consider switching to some other, Go-friendly, build automation system (but it seems that even with gb I can't easily do what I want: quickly generate some simple .go
file at every build). But I don't understand why it is so difficult to use generated Go files in Go programs. Generating simple code is following the Unix philosophy, and has been practiced since many decades (e.g. see goyacc inspired by the old yacc program).
NB: the rationale for go generate
explicitly mentions that:
It is not a goal of this proposal to build a generalized build system like the Unix
make(1)
utility.
and later
once things are settled, the author commits the generated files to the source repository,
(and this is not my use case)
PS. I only care about POSIX systems; I really don't care if my Go software cannot be built on Windows. And I tend to think that (contrarily to what go
command motivation explains), in my particular case, I do need some build automation tool. In my bastawigo toy project (GPLv3+), I am using make
(driving the go
command)
You can use build time flags to do this. This answer explains it for a different use case, but yours is identical to it. You should do something like this.
buildTime
go build -ldflags='-X buildTime="My build time output from a command"'
buildTime
is constant and equals to My build time output from a command
.My build time output froma a command
.Refer to the answer I linked for better explanation.
As other answers mentioned, you can inject the needed values into the binary at build time.
For example if we have these package variables:
// build flags
var (
BuildTime string
CommitHash string
GoVersion string
GitTag string
)
Then we can fill them using this bash script:
#!/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
Sample code took from this repo.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With