Build-Time Variables in Go
Setting variables at build-time provides valuable metadata to our application that wasn’t available when writing the code or even at runtime. We can control feature-flags, or build information, like a version number, without updating the Go-code constantly.
Table of Contents
Versioning a Go Project
Imagine we have version/version.go
in the main package github.com/benweidig/goapp
:
The three properties are identifying any build:
Version
: the version identifier of your application.CommitHash
: for reproducible builds, it’s important to know which commit was used to create a specific binary.BuildTimestamp
: a dynamic build-context value that provides info about the build itself. We could also use the build number of the CI/CD-pipeline, instead.
Of course, we could just update the Version
every time before building a new binary. But the CommitHash
is a value that can’t be set before committing the code itself: the hash of the commit used to build the application wouldn’t be the one set in the code. BuildTimestamp
depends on the build itself and can’t be set beforehand.
We will set these values at build-time instead of updating our code before compiling it with the help of linker flags. They help us to make our builds reproducible and automate the process of creating a meaningful version identifier. But be aware that the dynamic BuildTimestamp
will make the result of the compilation non-deterministic. Even though the correct source code is identified by the CommitHash
, the resulting binary won’t be the same due to the different BuildTimestamp
.
All these properties are needed to create reproducible builds. So we will set them at build-time instead of updating our code before compiling it with the help of linker flags.
Linker Flags to the Rescue
The Go build process – go build
– supports linker flags, that will be passed to the underlying go tool link
call, allowing us to set public variables in your code at build-time.
To pass a linker flag, we have to add the -ldflags
argument:
The linker flag we’re interested in is -X
, which sets a variable at link time:
This big caveat is that only string
values are allowed, as you might have already guessed from how the values are quoted.
In the case of our version.go
file the call would look like this:
Instead of writing the version directly in the call, we should use a script to derive the required values automatically.
Build Scripts
So far we’ve just moved the burden of writing the current version from the Go file to the build command.
Let’s automate that step with a script git
.
The other properties can also be derived in such a script:
The general concept of the script should be easily adaptable to your own CI/CD needs.
Personally, I use a Makefile
for my Git-helper Tortuga
.
Conclusion
Injecting dynamic values at build-time with linker flags is a powerful addition to your toolbelt. You can provide build information as shown in the article or use it to control feature flags, or anything else that depends on the build context itself, and not the information available at runtime.