Skip to content
Julius Kibunjia's Blog
Go back

Makefile Madness

5 min read

Table of Contents

Open Table of Contents

Conditional Assignment

The first one is not too difficult to understand, basically in Makefiles you can assign a value to a variable unless it already has a value in which case you just leave it alone. The way this looks is

DIR ?= ~/code/Python/

This is useful since it enables you to set default values for variables but ensures that in case the user already specified a value, to use that instead. Using conditional assignment allows your rules to be configurable but also not fail when a value isn’t supplied.

eval

It is very rare to hear eval in any language being spoken of positively given that it is very easy to misuse. Regardless, due to the nature of Makefiles as kind of an weird macro language, eval exists and is sometimes useful. To understand how we’ll go on a bit of a tangent.

If you’ve ever seen a Makefile you know that it roughly consists of a bunch of variables followed by sections describing how to perform certain tasks. The part that interests us are the task descriptions. The syntax is roughly:

rulename: prerequisites
  command1
  command2
  ...
  commandn

The section with the commands is not technically Make in the sense that it is treated as text that is just ‘preprocessed’ and then passed on to the relevant program to be executed. Suppose we want to do Make specific things like set variables in this section and have them persist in Make’s environment, eval comes in handy here:

py3-proj:
	$(eval DIR ?= ~/code/Python/)
	$(MAKE) -f $(AFILE) create-project DIR=$(DIR) TYPE=Python3 EDIT=$(EDIT)

What is happening above is first we checks if a DIR variable is already set (perhaps was specified on the command line) before setting a default. Then we call Make recursively (😎) to run a different rule with some ‘arguments’.

Changing SHELL

If I could only write about one interesting feature this would definitely be it. I remember reading about it in the GNU docs and thinking it was such a cool feature. The reason why is that it frees you from the restriction of using shell which also has it’s own unique set of problems.

SHELL = /usr/bin/python3
.ONESHELL:
chaos-game:
	import turtle
	from random import choice
	edges = [(-200, 200), (200, 200), (200, -200), (-200, -200)]
	current = choice(edges)
	last = edges[0]
	turtle.speed(0)
	turtle.penup()
	turtle.hideturtle()
	for i in range(5000):
	    while last == current:
	        current = choice(edges)
	    tx, ty = turtle.position()
	    new_x, new_y = ((current[0] + tx) / 2, (current[1] + ty) / 2)
	    turtle.setpos(new_x, new_y)
	    turtle.dot(8, "red")
	    last = current
	    current = choice(edges)

	turtle.done()

.ONESHELL from the docs:

If .ONESHELL is mentioned as a target, then when a target is built all lines of the recipe will be given to a single invocation of the shell rather than each line being invoked separately

.SHELLFLAGS is also a useful variable to set as it controls the arguments that are passed to the program in SHELL. By default it is set to -c.

Canned Recipes and call

Canned recipes allow you to define a sequence of commands that can be used in multiple recipes saving it in a variable. Combining this with the call function leads to something very useful. call is used to create new parameterized functions by writing an expression in a variable (using define for instance) then expanding it with temporary variables $1, $2 and so forth that hold the arguments the call was made with.

Here is a useful example that adds simple notifications depending on the exit status of the last shell command.

define notify =
if [[ $$? -eq 0 ]];then
  notify-send -t 5000 $1
else
  notify-send -t 5000 $2
fi
endef

# example usage
cap-screen:
	# Take screenshot of selection
	scrot -q 100 -s
	$(call notify, "Screenshot taken", "Failed to take screenshot")

Going Further

Turns out that Make is a lot more flexible than it appears at first glance. There’s a ton of more lesser known features I have not covered here but are in the official documentation for GNU Make. Take a look at it and feel free to send me an e-mail if you find another gem.



Previous Post
Working With Git
Next Post
Colouring arbitrary shell output