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 make
command. 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