Makefiles with Go

Go Makefile

When you’re often working on large projects involving Golang, it’s often time consuming to keep typing the build and test commands go build xx, go test xx, repeatedly. And often these commands itself may become non-trivial.

Makefiles provide a very effective way to automate writing tasks for our application.

Let’s understand how we can easily set up our workflow using makefiles!

NOTE: While the make toolchain is widely popular, it is limited to only UNIX platforms. So if you’re using Windows, you may need to install a Linux environment inside Windows (such as msys / WSL) to use make commands.

Using Makefiles in Go

The main functionality use-case for makefiles is to easily run different tasks using “tags”. Using tags, make TAGNAME will simply run the task corresponding to TAGNAME.

So let’s assume a sample program main.go. If you want to build this file, you’d normally run this command:

go build main.go

And if you want to execute the program directly, you’d do this:

go build -o main.out main.go
./main.out

However, with a single makefile, you can create 2 tasks build and run instead.

BINARY_NAME=main.out

build:
    go build -o ${BINARY_NAME} main.go

run:
    go build -o ${BINARY_NAME} main.go
    ./${BINARY_NAME}

clean:
    go clean
    rm ${BINARY_NAME}

Now, you can run the build and run tasks like this:

make build
make run

If you want to add a default tag which would call the necessary tasks, we can use the all tag:

BINARY_NAME=main.out

all: build test

build:
    go build -o ${BINARY_NAME} main.go

test:
    go test -v main.go

run:
    go build -o ${BINARY_NAME} main.go
    ./${BINARY_NAME}

clean:
    go clean
    rm ${BINARY_NAME}

This will now build and test our main.go program, when we run:

make

See how much easy our work has now become? That’s why these build toolchains are extremely useful!

Notice that you can use shell variables in makefiles. The variable BINARY_NAME is actually main.out, so the dollar sign will perform the variable substitution.

And the best part is that you don’t need to stop here. You can run as many complex tasks as you need.

For example, if your project has multiple dependencies that you need to install using go get package-name, you could automate that too!

We could create a separate task called deps, which will install all the relevant packages directly.

For instance, if my project needs the Gorilla websocket library, I could have a task like this:

deps:
    go get github.com/gorilla/websocket

And when I run:

make deps

All dependencies get installed.

Hopefully this post gives you an idea of how you could automate build tasks and setup your Golang project workflow quickly. Until next time!