Glossary

Select one of the keywords on the left…

UtilitiesMake

Reading time: ~10 min

In a typical coding project, some files will be processed to produce other files as output. For example, programs written in C are compiled into binary executable files. LaTeX files are compiled to PDF files. Python files process raw data files and produce clean versions of the data. The clean versions of the data are in turn used to produce analysis and visualizations.

We call the output files target files, and the input files are called source files. The source-target relationships of a project are a crucial component of the structure of the project, because the processing steps are required to reproduce the analysis or update the target files to reflect changes in the source. However, this important structure is not usually apparent from the source and target files themselves.

There are at least two ways to address this problem: (1) document the processing steps in a README file, or (2) write a Makefile, which can be processed by a command-line utility called make to actually carry out the processing steps. The second approach documents the source-target relationships and makes them executable.

Suppose, for example, that we have a file called raw-data.csv which contains some data we are meant to analyze. We write some code in a file called clean-data.py to process that data and write a file called clean-data.csv. Then we run model.py to do some analysis and come up with a model that we save in a file called model.pkl. Our Makefile would look something like this:


all : model.pkl

model.pkl : model.py clean-data.csv
    python model.py

clean-data.csv : clean-data.py raw-data.csv
    python clean-data.py

clean :
    rm model.pkl
    rm clean-data.csv

.PHONY all

The basic formula for a Makefile entry is

target : dependencies
    recipe

The command-line function make looks at whether the dependencies have changed since the target was last built, if they have then it runs the commands in the corresponding recipe.

The target can either be an actual target file or a phony target, which is a name used to refer to a given processing step. The phony targets should be declared in a line that begins .PHONY and lists the phony targets. It's conventional to include the phony targets all and clean. These correspond to the operations "build everything" and "remove the target files".

Specific targets can be invoked at the command line by running make targetname. For example, make all builds everything and make clean removes the target files. The target may be omitted, in which case it defaults to the first target in the file.

Exercise
Write a Makefile which runs pdflatex my-document.tex to produce my-document.pdf whenever my-document.tex changes. Include the phony targets all and clean. Assume that pdflatex will produce auxiliary files my-document.aux and my-document.log.

Solution. We remove the auxiliary files with clean and make all include just the target my-document.pdf:


all : my-document.pdf

my-document.pdf : my-document.tex
    pdflatex my-document.tex

clean :
    rm my-document.aux
    rm my-document.log

.PHONY all clean

To reveal more content, you have to complete all the activities and exercises above. 
Are you stuck? or reveal all steps

Next up:
Docker
Bruno
Bruno Bruno