Wednesday, 13 September 2017

Initialising String Literals at Compile Time in Go

Recently I was working on a service and realised that we had no established way of querying it for its version information. Previously, on an embedded device, I have written a file at build time to be read by the service at run time, with the location set by an env var. It would also be possible to set the version in an env var itself but these are overridable

  However a colleague suggested injecting the version string into the binary itself at compile time so I decided to investigate this novel approach.

go tool link specifies that the -X argument allows us to define arbitrary string values

go tool link
  -X definition
        add string value definition of the form

go build help explains that there is an 'ldflags' option which allows the user to specify a flag list passed through to go tool link

go build help
    -ldflags 'flag list'
        arguments to pass on each go tool link invocation.

So we can pass a string definition through the go command (build, run etc)!

In the above program we can see that we define a variable foo in package main.
Thus the fully qualified name of this variable is, thus this is the name we pass.

$ go run main.go
injected var is []

$ go run -ldflags '-s -X' main.go
injected var is [bar]

This can be used in novel ways to inject the output of external commands at compile time such as the build date.

$ go run -ldflags "-X '$(date -u '+%Y-%m-%d %H:%M:%S')'" main.go
injected var is [2017-09-13 13:44:59]

This is a nice feature that I'm sure has applications beyond my reckoning at this time. In my use case it makes for a compact, neat solution, however it does cause some indirection when reading the code making it harder to follow so I would argue should be used sparingly. It has the nice quality of being immutable over env vars which could potentially be rewritten. Anyhow it is a pretty cool linker feature!

No comments:

Post a Comment