CloudFlare's DNS server, RRDNS, is written in Go and the DNS team used to generate a file called version.go in our Makefile. version.go looked something like this:

// THIS FILE IS AUTOGENERATED BY THE MAKEFILE. DO NOT EDIT. // +build make package version var ( Version = "2015.6.2-6-gfd7e2d1-dev" BuildTime = "2015-06-16-0431 UTC" )

and was used to embed version information in RRDNS. It was built inside the Makefile using sed and git describe from a template file. It worked, but was pretty ugly.

Today we noticed that another Go team at CloudFlare, the Data team, had a much smarter way to bake version numbers into binaries using the -X linker option.

The -X Go linker option, which you can set with -ldflags , sets the value of a string variable in the Go program being linked. You use it like this: -X main.version 1.0.0 .

A simple example: let's say you have this source file saved as hello.go .

package main import "fmt" var who = "World" func main() { fmt.Printf("Hello, %s.

", who) }

Then you can use go run (or other build commands like go build or go install ) with the -ldflags option to modify the value of the who variable:

$ go run hello.go Hello, World. $ go run -ldflags="-X main.who CloudFlare" hello.go Hello, CloudFlare.

The format is importpath.name string , so it's possible to set the value of any string anywhere in the Go program, not just in main. Note that from Go 1.5 the syntax has changed to importpath.name=string . The old style is still supported but the linker will complain.

I was worried this would not work with external linking (for example when using cgo) but as we can see with -ldflags="-linkmode=external -v" the Go linker runs first anyway and takes care of our -X .

$ go build -x -ldflags="-X main.who CloudFlare -linkmode=external -v" hello.go WORK=/var/folders/v8/xdj2snz51sg2m2bnpmwl_91c0000gn/T/go-build149644699 mkdir -p $WORK/command-line-arguments/_obj/ cd /Users/filippo/tmp/X /usr/local/Cellar/go/1.4.2/libexec/pkg/tool/darwin_amd64/6g -o $WORK/command-line-arguments.a -trimpath $WORK -p command-line-arguments -complete -D _/Users/filippo/tmp/X -I $WORK -pack ./hello.go cd . /usr/local/Cellar/go/1.4.2/libexec/pkg/tool/darwin_amd64/6l -o hello -L $WORK -X main.hi hi -linkmode=external -v -extld=clang $WORK/command-line-arguments.a # command-line-arguments HEADER = -H1 -T0x2000 -D0x0 -R0x1000 searching for runtime.a in $WORK/runtime.a searching for runtime.a in /usr/local/Cellar/go/1.4.2/libexec/pkg/darwin_amd64/runtime.a 0.06 deadcode 0.07 pclntab=284969 bytes, funcdata total 49800 bytes 0.07 dodata 0.08 symsize = 0 0.08 symsize = 0 0.08 reloc 0.09 reloc 0.09 asmb 0.09 codeblk 0.09 datblk 0.09 dwarf 0.09 sym 0.09 headr host link: clang -m64 -gdwarf-2 -Wl,-no_pie,-pagezero_size,4000000 -o hello -Qunused-arguments /var/folders/v8/xdj2snz51sg2m2bnpmwl_91c0000gn/T//go-link-mFNNCD/000000.o /var/folders/v8/xdj2snz51sg2m2bnpmwl_91c0000gn/T//go-link-mFNNCD/000001.o /var/folders/v8/xdj2snz51sg2m2bnpmwl_91c0000gn/T//go-link-mFNNCD/go.o -g -O2 -g -O2 -lpthread 0.17 cpu time 33619 symbols 64 sizeof adr 216 sizeof prog 23412 liveness data

Do you want to work next to Go developers that can always make you learn a new trick? We are hiring in London, San Francisco and Singapore.