Automatically build software / libraries / documents by specifying dependencies via a Makefile
Originally created by Stuart Feldman in 1976 at Bell Labs
Almost universally available (all flavors of unix / linux / osx)
A Makefile
provides a list of target files along with their dependencies and the steps necessary to generate each of the targets.
target1: depend1 depend2 depend3 ...
step1
step2
step3
...
depend1: depend1.1
step1
step2
paper.html: paper.Rmd Fig1/fig.png Fig2/fig.png
Rscript -e "library(rmarkdown);render('paper.Rmd')"
Fig1/fig.png: Fig1/fig.R
cd Fig1;Rscript fig.R
Fig2/fig.png: Fig2/fig.R
cd Fig2;Rscript fig.R
Because the Makefile
specifies the dependency structure make
knows when a file has changed (by examining the file’s modification timestamp) and only runs the steps that depend on the file(s) that have changed.
After running make
the first time, I edit paper.Rmd
, what steps run if I run make
again?
What about editing Fig1/fig.R
?
Like R or shell scripts or any other language we can define variables
R_OPTS=--no-save --no-restore --no-site-file --no-init-file --no-environ
Fig1/fig.png: Fig1/fig.R
cd Fig1;Rscript $(R_OPTS) fig.R
By default is you run make
without arguments it will attempt to build the first target in the Makefile
whose name does not start with a .
. By convention we often include an all
target which explicitly specifies how to build everything within the project.
all
is an example of what is called a phony target - because there is no all
file in the directory. Other common phony targets:
clean - remove any files created by the Makefile, restores to the original state
install - for software packages, installs the compiled programs / libraries / headers
We list all phony targets by including the line like the following:
.PHONY: all clean all
$@
the file name of the target
$<
the name of the first dependency
$^
the names of all dependencies
$(@D)
the directory part of the target
$(@F)
the file part of the target
$(<D)
the directory part of the first dependency
$(<F)
the file part of the first dependency
Often we want to build several files in the same way, in these cases we can use %
as a special wildcard character to match both targets and dependencies.
So we can go from
Fig1/fig.png: Fig1/fig.R
cd R;Rscript fig.R
Figs2/fig.png: Fig1/fig.R
cd R;Rscript fig.R
to
Fig%/fig.png: Fig%/fig.R
cd $(<D);Rscript $(<F)
all: paper.html
paper.html: paper.Rmd Fig1/fig.png Fig2/fig.png
Rscript -e "library(rmarkdown);render('paper.Rmd')"
Fig%/fig.png: Fig%/fig.R
cd $(<D);Rscript $(<F)
clean:
rm -f paper.html
rm -f Fig*/*.png
.PHONY: all clean
Live Demo