Add post: A Makefile for Golang CLI tools
This commit is contained in:
parent
628065f4c2
commit
31f054881a
126
content/posts/2015-10-03-a-makefile-for-golang-cli-tools.md
Normal file
126
content/posts/2015-10-03-a-makefile-for-golang-cli-tools.md
Normal file
|
@ -0,0 +1,126 @@
|
|||
+++
|
||||
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-clie-tools"
|
||||
+++
|
||||
|
||||
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. (I'm not even sure I'm going to use this a my next Dungeons & Dragons session.)
|
||||
|
||||
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
|
||||
|
||||
## The Makefile
|
||||
|
||||
Makefiles have always been scary to me. Lot's of magic and weird syntax and I never had a need or 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.
|
Loading…
Reference in New Issue
Block a user