Note: I’ve received feedback on this post and written an update, which you can read here
It’s no secret I love the power and simplicity of Go. To further train my skills I wrote a simple app that will roll dice from the command line, because you know, that’s very useful.
There are two goals for me in this project right now: make it trivial to use compile time variables and have a Makefile
for easy
compilation, installation and clean up. I’m sure I’ll think of other features to try. These will get their own posts.
Compile time variables#
The classic example for compile time variables is setting a version number and build date for the binaries you compile. You could manually edit code before each compile, because you would never forget to do that.
var (
Version = "1.0.0"
BuildTime = "2015-10-03T11:08:49+0200"
)
Luckily there’s a nice alternative provided by Go: the link
docs command allows you to set
string variables at compile time with the -X
option. Let’s take a look at our code and build command.
var (
Version string
BuildTime string
)
Compilation would look like this:
go build -ldflags "-X github.com/ariejan/roll/core.Version=1.0.0 -X github.com/ariejan/roll/core.BuildTime=2015-10-03T11:08:49+0200" main.go
Note: this is the format used with Go 1.5.1, previous versions do not use the =
sign, instead separate the variable and value with a space.
The Makefile#
Makefiles have always been scary to me. Lot’s of magic and weird syntax and I’ve never had the need nor the desire to dive into them. As it turns out,
Makefiles can be very useful. Let’s start by building one for the roll
project.
First, let’s start with the build command that passes in Version
and BuildTime
and refactor it so it becomes more managable and we can
easily set both variables to proper values.
# This is how we want to name the binary output
BINARY=roll
# These are the values we want to pass for Version and BuildTime
VERSION=1.0.0
BUILD_TIME=`date +%FT%T%z`
# Setup the -ldflags option for go build here, interpolate the variable values
LDFLAGS=-ldflags "-X github.com/ariejan/roll/core.Version=${VERSION} -X github.com/ariejan/roll/core.BuildTime=${BUILD_TIME}"
all:
go build ${LDFLAGS} -o ${BINARY} main.go
Now, if you run make all
your binary will be compiled with the proper variables passed in. There are a few caveats here, though.
Each Make target (we only named all
) will check for an output file named all
and decide if it needs compiling or not. This is
how Make can speed up large builds - by not compiling things that don’t need compiling. In the case of our project I want to make two
changes: properly name our target after the binary we create and make sure we only re-compile if any of the Go files have changed.
First, let’s rename our build target to the name of our binary. Also, set it as the default target and make sure make all
will also
compile the binary for us.
.DEFAULT_GOAL: $(BINARY)
$(BINARY):
go build ${LDFLAGS} -o ${BINARY} main.go
Next let’s get a list of all go source files we want Make to watch, for this we’ll rely on find
.
SOURCEDIR=.
SOURCES := $(shell find $(SOURCEDIR) -name '*.go')
Remember how each Make target corresponds to a file on disk? Make will check if that target file exists or was changed. This means we can add the list of source go files as dependencies to the build task. If any of the source files were changed, make will re-run the task.
This is the Makefile so far:
SOURCEDIR=.
SOURCES := $(shell find $(SOURCEDIR) -name '*.go')
BINARY=roll
VERSION=1.0.0
BUILD_TIME=`date +%FT%T%z`
LDFLAGS=-ldflags "-X github.com/ariejan/roll/core.Version=${VERSION} -X github.com/ariejan/roll/core.BuildTime=${BUILD_TIME}"
.DEFAULT_GOAL: $(BINARY)
$(BINARY): $(SOURCES)
go build ${LDFLAGS} -o ${BINARY} main.go
For fun we can add two more tasks: install
and clean
. Because both of these do not result in a file in our repository (like the build
command), we
mark these targets with .PHONY
, telling Make not to expect a file to appear.
SOURCEDIR=.
SOURCES := $(shell find $(SOURCEDIR) -name '*.go')
BINARY=roll
VERSION=1.0.0
BUILD_TIME=`date +%FT%T%z`
LDFLAGS=-ldflags "-X github.com/ariejan/roll/core.Version=${VERSION} -X github.com/ariejan/roll/core.BuildTime=${BUILD_TIME}"
.DEFAULT_GOAL: $(BINARY)
$(BINARY): $(SOURCES)
go build ${LDFLAGS} -o ${BINARY} main.go
.PHONY: install
install:
go install ${LDFLAGS} ./...
.PHONY: clean
clean:
if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi
It’s a basic Makefile
that makes compiling your Golang command line tools a whole lot easier. Enjoy, and stay tuned for more
posts on Golang and Makefiles.
You can find the code for Roll at https://github.com/ariejan/roll.