Makefiles
The make
system wants to know:
- What file you want to make,
- Which other files it depends on, and
- How to build the file.
Start with a basic test-area.
1mkdir make_test ; cd $_
2printf "%s:\n" README.md > Makefile
3printf "\t%s\n" 'echo "Basic makefile example." > $@' >> Makefile
4make
NB: Always tell make
how to build files with a \t
(tab) character.
Using four spaces will not work!
Dependency Files
Now we've made a README.md
file, we can show how a makefile looks in the README:
1README.md: Makefile
2 echo "Basic makefile example." > $@
3 echo "" >> $@
4 echo '```' >> $@
5 cat $< >> $@
6 echo '```' >> $@
Note the order:
- The first thing is the file you want, then a colon (
:
). - After the colon, any file it depends on.
- Finally, the shell commands to execute.
Strange Sigils
Notice that the file above can print into the README by using echo "" >> $@
.
The $@
stands for 'the file which we want', and $<
stands for 'the first dependency file'.
The make
program starts by replacing those variables, and the result it:
1README.md: Makefile
2 echo "Basic makefile example." > README.md
3 echo "" >> README.md
4 echo '```' >> README.md
5 cat Makefile >> README.md
6 echo '```' >> README.md
Sigil | Meaning |
---|---|
$@ |
The file we want |
$< |
First dependency file |
$^ |
All dependency files |
$(@F) |
Filename of the file we want |
$(@D) |
Directory path of the file we want |
$(<F) |
Filename of the first dependency |
$(@D) |
Directory path of the first dependency |
Basic Variables
You can assign a variable normally, but must refer to it in brackets.
1storage_directory = backups
2
3README.md: Makefile
4 echo "Basic makefile example." > $@
5 echo "" >> $@
6 echo '```' >> $@
7 cat $< >> $@
8 echo '```' >> $@
9
10$(storage_directory)/README.md: README.md
11 mkdir $(@D)
12 cp $< $@
Now you can tell make
to create the backup:
1make backups/README.md
Command Variables
The backup README.md
could be named after the current minute of the day, using date +%M
.
This allows up-to-the-minute backups:
1current_minute != date +%M
2
3storage_directory = backups
4
5README.md: Makefile
6 echo "Basic makefile example." > $@
7 echo "" >> $@
8 echo '```' >> $@
9 cat $< >> $@
10 echo '```' >> $@
11
12$(storage_directory)/backup_$(current_minute).md: README.md
13 mkdir $(@D)
14 cp $< $@
...but the repeated use of mkdir
is causing an error, because that directory already exists.
We can solve this by using mkdir -p
.
Phony Targets
But we don't want to look up the current minute of the day to make backups.
Better to just say make backup
.
However, this will confuse make
, because make
thinks everything is a file, so it would try to make a file called backup
.
The solution is to tell make
that backup
is a phony target.
1 [ ... ]
2
3.PHONY: backup
4backup: $(storage_directory)/backup_$(current_minute).md
5$(storage_directory)/backup_$(current_minute).md: README.md
6 mkdir -p $(@D)
7 cp $< $@
Now run make backup
to create an up-to-date backup.
Order
Makefile thinks like this:
- Fill in all the variables in the file, from top to bottom.
- If variables are missing, go through the file again.
- Figure out the order the files should be built in.
In this case, the makefile can see that backup
depends on the current backup file (with the minute in the filename), which depends on the README.md
file, which depends on the Makefile itself.
1
2┌──────────────────────┐
3│ Makefile │
4└──────────────────────┘
5 │
6 │
7 ▼
8┌──────────────────────┐
9│ README.md │
10└──────────────────────┘
11 │
12 │
13 ▼
14┌──────────────────────┐
15│ backups/backup_06.md │
16└──────────────────────┘
17 │
18 │
19 ▼
20┌──────────────────────┐
21│ backup │
22└──────────────────────┘