In Makefiles: Part 1 — A Gentle Introduction we learned a few tricks concerning the basics of Makefiles.
Today, we will learn some of the more confusing symbols used in Makefiles’ syntax, commonly seen around the web in Makefiles.
We will use this to create nice looking basic webpages from a simple markup language.
Then we will expand our possibilites such that we can make other formats than webpages!
Last Time
A quick recap of last time! Let’s look at our final makefile from that session (with a small change to accomodate our upcoming fourth rule):
.PHONY: first_rule second_rule \ third_rule first_rule: @echo "Hello World!" second_rule: @echo "Helloer World!" third_rule: second_rule @echo "Helloest World!"
In quick summary, from this we learned:
@
can be used to silence output we are not interested in,make <target name>
can be used to run a given rule,- we can list dependencies of targets in order to have then run as well,
.PHONY
is used on targets which should not output files directly,- we can escape newlines in order to have long lines.
So when we ran e.g. make third_rule
using the above makefile, we got the output:
Helloer World!
Helloest World!
Now let’s look at something new!
Common Syntax Seen Around The World
If you have looked at a few makefiles you might have come across the symols $@
, $<
, and $^
. These useful symbols are used quite a lot. What do they do? Let’s find out!
Fourth Rule: Breaking Down the Dollar
The abovementioned symbols are called automatic variables. What do they automagically do?
One way to figure this out is to use our friend @echo
.
Let’s make an addition to our makefile, a fourth rule (don’t forget to add it to our .PHONY
list):
fourth_rule: first_rule third_rule
Run this, and see that we now have our original output:
Hello World!
Helloer World!
Helloest World!
Nice.
Trick 1: $@
Now let’s add a recipe line, using $@
:
fourth_rule: first_rule third_rule @echo "I am: '$@'."
You might already suspect the relevant output line, which says:
I am 'fourth_rule'.
Nice, a new trick!
This automatic variable will automatically replace itself with the name of the target that uses it.
Trick 2: $<
Let’s try the next one, $<
:
fourth_rule: first_rule third_rule @echo "I am: '$@'." @echo "I depend on '$<."
This new line’s output is:
I depend on 'first_rule'.
We see what’s going on here!
This automatic variable substitutes to the first dependency.
Trick 3: $^
Closely related is the next: $^
:
fourth_rule: first_rule third_rule @echo "I am: '$@'." @echo "I depend on '$<." @echo "In total I depend on '$^."
Related output:
In total I depend on 'first_rule third_rule'.
Aha, makes sense that this exists when the last trick only listed the first dependency!
This automatic variable substitutes into the whole dependency list.
Alright, that wasn’t too bad! After seeing these automatic variables a few times, they will become second nature, and reading new makefiles will be easy.
Now let’s use these new tricks in order to make something useful!
Making a Webpage
Have you heard about the great tool pandoc? Free, open-source!
What can it do? Say you have a .docx
file, a .md
file, or a .html
file. It can convert this to one another, or to .pdf
, or to presentation slides, and much more! A very convenient tool (it supports a lot more than these formats, check out the website).
We’ll be testing this by converting markdown files (.md
) into webpages (.html
), and add some css to make it look nice.
A Simple Markdown File
The markdown file is not important here, so we will use this simple file webpage.md:
# Hello Webworld! This is body text. ## This is a Smaller Heading! - Bullet point 1 - Bullet Point 2
Making it Into a Webpage
Before we involve the makefile, let’s look at how we’d do it manually:
pandoc -o webpage.html webpage.md
The output looks like this:
Hey, at least it works… let’s make it look a bit nicer.
Steal this css from GitHub user killercup and invoke pandoc like this:
pandoc -o webpage.html --css pandoc.css --self-contained --quiet
Note 1: Self contained means that the css is embedded in the html file (such that even if we deleted the css file it would still work).
Note 2: Quiet is just to suppress a warning that is not important to us.
We now have this:
Better!
Let’s use our makefile to automatically create webpages such as this from all the .md
files in our folder, and use the css file we tell it to from the command line!
Make Does the Job
Add a new rule to the top of our makefile (since we add it to the top, it will be the rule that runs if we add no target, i.e. write only make):
web: $(TARGET_FILES)
We are setting up this such that the web rule will be run if any of its target files are changed. This is a list of .html
files.
How do we make this? Add this variable at the top of the makefile:
Trick 4: patsubst
TARGET_FILES := $(patsubst %.md,%.html,$(wildcard *.md))
This neat function can do a wildcard search of files, and then swap one thing for another in each file found.
In the example above, the third argument finds all the files, the first argument is what to look for in the matches, and the second argument is what to replace the matches with.
Trick 5: %
Now we have a rule that lists a number of .html
files as a dependency.
This means we must provide a rule that tells make how to produce .html
files:
%.html: %.md pandoc -o $@ $<
In the above example, if the input was super_input.html
, the dependency would automatically become super_input.md
.
So this trick is using % to create a target .html
file out of our dependency .md
file automatically!
So now, if the dependency .md
file changes, this rule will fire off when running make
(or make web
).
The recipe is:
pandoc -o $@ $<
.
From earlier in this blog post, we know that this translate to:
pandoc -o super_input.html super_input.md
.
This is how we created the webpage earlier!
Cool, we are at the point where we can write make
, and all our .md
files will be turned into webpages!
Let’s use enable us to execute make CSS=pandoc.css
to make all conversions use this css file.
Trick 6: Conditionals
Add this:
ifdef CSS PANDOC_OPTS = --css $(CSS) --self-contained --quiet else PANDOC_OPTS = endif
This syntax is close to what we can see in many programming languages.
Now we modify our .html
making rule to this:
%.html: %.md pandoc -o $@ $(PANDOC_OPTS) $<
Now if we can choose to add css when we invoke make or not!
One last trick before we take a break from makefiles.
Let’s let the user choose what the output should be. Instead of always using .html
, we could e.g. say FORMAT=pdf
.
How would we do this?
Trick 7: Default Variables
Add this to the top of our makefile:
FORMAT ?= html
This tells make that if we specify nothing, it will use html as the format.
If we do tell it via the command-line e.g. make CSS=pandoc.css FORMAT=pdf
, this will override the default.
Let’s remove the unused parts of the makefile, and substitute .html
with the general .$(FORMAT)
. We get our final result:
.PHONY: web FORMAT ?= html TARGET_FILES := $(patsubst %.md,%.$(FORMAT),$(wildcard *.md)) ifdef CSS PANDOC_OPTS = --css $(CSS) --self-contained --quiet else PANDOC_OPTS = endif web: $(TARGET_FILES) %.$(FORMAT): %.md pandoc -o $@ $(PANDOC_OPTS) $<
Now pandoc will try to output any format we tell it to.
Take care until next time, when we’ll look at using makefiles for programming embedded firmware!