133 lines
5.3 KiB
Markdown
133 lines
5.3 KiB
Markdown
+++
|
|
date = "2015-10-03"
|
|
title = "A makefile for Golang CLI tools"
|
|
tags = ["golang"]
|
|
description = "Golang is very useful for creating command line interfaces. It does get complicated when you also want to set variables at compile time. I've made a Makefile for that."
|
|
slug = "a-makefile-for-golang-cli-tools"
|
|
+++
|
|
|
|
_Note: I've received feedback on this post and written an update, which you can read [here](https://ariejan.net/2015/10/12/building-golang-cli-tools-update/)_
|
|
|
|
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](https://golang.org/cmd/link/) 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](https://github.com/ariejan/roll).
|