Project Build

Introduction #

When working with Go projects, sooner or later, you will find the need for a build system. A build system doesn’t need to be this complex enterprise monster; in fact, a simple bash script could be considered a build system. You only need such a system to run the operations or tasks you need to compile, build binaries, run tests, etc.

Go has no sponsored tool except for the go binary.

The aim of this post is not to go into details on the different build tools but rather give you some ideas and also ready to copy and paste code you can use to get started.

Below, I will cover Bash, Make, and Task. Feel free to skip directly to the information that feels more relevant.

But I’m using a CI; do I need a build tool?

Well yes.

You still want/need to run several commands on your development machine, and it is convenient to automate these. The same automation can also be used in the CI, or you can keep it separate, but running a command like tool build, which will run tests, linters and compilation, is much more convenient than trying out all the separate commands.

Using Bash #

Bash is not the best language, but it runs almost everywhere. Below, I’ll present a script ready for copy and pasting into your project.

The basics of it are: #


Commands provided: #

To install #

To install, download the script below, name it build.sh and run chmod +x ./build.sh to make it executable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

#!/usr/bin/env bash

LINTCMD="$GOPATH/bin/golangci-lint"

check () {
  go mod download
  go fmt ./...
  go vet ./...
  go mod tidy
}

lint () {
  if ! "$LINTCMD" -v &> /dev/null ;
  then
    go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
  else
    "$LINTCMD" run ./...
  fi
}

test () {
  go test ./...
}

build_binary () {
  go build ./...
  GOBIN=`pwd` go install ./...
}

CMD="$1"
shift

case "$CMD" in
   lint )
     check
     lint
     ;;
   test )
     test
     ;;
   build )
     check
     build_binary
     ;;
   * )
     echo "Wrong command"
     exit 1
esac

Using Make #

Make is another ubiquitous build tool for many languages and frameworks. If you think make is not for your project, think of this: Make is used to build Linux. This is a vast and complex project with many facets.

I think everyone should learn Make, even if it is just to expand their knowledge.

To learn more about Make, see the Make File Tutorial or refer to the GNU manual online.

To install, download the script below, save it into a file called “Makefile”, then run make.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
.DEFAULT_GOAL := build
.PHONY:fmt vet build

fmt:
	go fmt ./...

vet: fmt
	go vet ./...

test: vet
	go test ./...

build: vet
    go mod tidy
	go build

clean:
	go clean

Note: When copying the makefile content, you may need to redo the tabs.

Make by default uses tabs and not spaces. This can be configured with the .RECIPEPREFIX variable, as explained in the Makefile Documentation

Reading the manual, is your friend here. If you are going to use Make, set a few reading sessions aside to get familiar with it.

Using Task #

If Yaml build files are your thing, you will like Task. You configure your build using a Taskfile.yml and then run task <cmd> <cmd> ...

Conclusion #

I am always torn between Makefile and custom bash scripts for Go projects. What I have found in the past is that I start out with a Makefile and if I want to do more complex stuff, I fall back to bash.

Whether you stay with a bundle of commands, automate using scripts, or build systems depends on your experience, skills, and personal whims; I recommend exploring some of the above ideas and seeing how it works out for you.

You may also enjoy