Automating Golang Builds with Makefiles

Automating Golang Builds with Makefiles

Make is a Unix utility that can be used to build and maintain groups of programs (and other types of files) from source code. It is accessible as make command in unix shells. When running the command for automation, you need to provide a Makefile. The utility is traditionally used for compiling code (C/C++), but they can also be used to automate common commands.

A Makefile will contain a set of rules that can get you up and running within no time. It saves time and effort therefore improving the overall productivity of a team. We will look at the important pieces that accelerates automation with Makefiles.

To begin with, there are two pieces in this equation: The make commandline tool and the Makefile. So far, we need to be aware that a Makefile contains automation rules while make tool executes them.

Example Golang Automation: Simple Smart Contract App

The Golang project we will work on has a couple of iterative tasks. For instance, in-order to execute a task, a programmer has to type very long commands on the terminal. One annoying thing is that executing these might be iterative, therefore, exhausting. In this episode, we will automate the following tasks.

  • Format code
  • Running tests
  • Compiling smart contracts
  • Building binaries
  • Setting up dependencies

Our center of interest will be automating the processes above using Makefiles. However, we will first take a moment to learn G

Makefiles

A Makefile structure resembles that of a YAML file and uses a declarative paradigm.

Syntax of a makefile

A Makefile has a pretty simple syntax. Once you have some basic programming knowledge, you can become the Zeus of Makefiles. The anatomy is basically a declarative format as follows:

target: dependencies
    command
    command
    command
  • target - Name of an action we would like to carry out.
  • dependencies - These are optional set of rules/targets that need to be executed first. They are only included when the target will need them.
  • Commands - These are usually written after a tab. They are the commands that the user wants to automate.

Executing a makefile

As mentioned, we use the makecommand. It takes in a parameter of the target that we would like to run. Eg.

$ make # run the default/main target
$ make clean
$ make test

Basic Syntax

Comments

Comments are written using #. The statements after the comment symbol will be ignored by the parser

# This is a comment

Standard output

The @ symbol is used to disable displaying the output to stdout. This is only used when the user does not want the output to be visible on the terminal. For instance, check the target mute below.

hello: # Target that prints 
  echo "Hello World" # Prints Hello World to the Screen

mute: # Target that does not print
  @echo "Hello World" # No output

Variables

Setting a variable in a Makefile is done using = symbol. It takes the format VARIABLE = value. The example below shows declaring a variable GOVERSION.

GOVERSION = 1.19

Variable setting example 1.0

HELLO = 'hello'
HELLO_WORLD = $(HELLO) world  # hello world

Variable setting example 1.1

To expand the value of HELLO_WORLD during assignment, we use the symbols :=

HELLO = 'hello'
HELLO_WORLD = $(HELLO) world  # hello world

HELLO = 'hi' # change the value of the greeting
HELLO_WORLD := $(HELLO) world  # hello world

Variables can be accessed using either $() or ${} syntax as shown in the example above.

Targets

Basically, we can consider targets to be named routines in a makefile. In Makefiles, the target all is run by default.

The all target

Making multiple targets and you want all of them to run? Make an all target.

all: setup compile # Installs dependencies and compiles

setup:
    go mod download # Downloads dependencies

compile:
    go build -o ./bin/app # Compile the source

clean:
    rm -f ./bin/app # Deletes the binary

Executing the targets

Now that we have automated a simple workflow. Let's see how to run it. The following terminal snapshot explains.

$ make # The target all will be executed
$ make setup # This will only run setup target
$ make compile # This will only run compile target

Defining a custom default target

At times, we might not want want to use the all target. This is a proof that makefiles are very customisable to meet one's tastes. To define a custom default target, we reassign the .DEFAULT_GOAL variable by giving it a value of the new target that should run by default.

.DEFAULT_GOAL := hello

hello:
 echo "Hello World"

PART II

In the next chapter, I will summarise by automating a real-life Golang project, talk about the usecases and best practises when using Makefiles.

Appreciation

Thank you for reaching here. Please share your comments and reach out to me on Twitter