Makefiles: Part 2 — Makefiles Can Make Webpages?

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 '$&lt;."

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 '$&lt;."
@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 $@ $&lt;

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) $&lt;

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) $&lt;

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!

Related Posts