I learned to use the command line utility make
in the context of building C programs. The program make
reads an input file to tell it how to make things. To make a C program, you compile the source files into object files, then link the object files together.
You can tell make
what depends on what, so it will not do any unnecessary work. If you tell it that a file foo.o
depends on a file foo.c
, then it will rebuild foo.o
if that file is older than the file foo.c
that it depends on. Looking at file timestamps allows make
to save time by not doing unnecessary work. It also makes it easier to dictate what order things need to be done in.
There is nothing about make
that is limited to compiling programs. It simply orchestrates commands in a declarative way. Because you state what the dependencies are, but let it figure if and when each needs to be run, a makefile is more like a Prolog program than a Python script.
I have a client that needs a dozen customized reports, each of which requires a few steps to create, and the order of the steps is important. I created the reports by hand. Then, of course, something changed and all the reports needed to be remade. So then I wrote a Python script to manage the next build. (And the next, and the next, …). But I should have written a makefile. I’m about to have to revise the reports, and this time I’ll probably use a makefile.
Here’s a very simple example of using a makefile to create documents. For more extensive examples of how to use makefiles for a variety of tasks, see How I stopped worrying and loved Makefiles. [1]
Suppose you need to create a PDF and a Microsoft Word version of a report from a LaTeX file [2].
all: foo.pdf foo.docx foo.pdf: foo.tex pdflatex foo.tex foo.docx: foo.tex pandoc foo.tex --from latex --to docx > foo.docx clean: rm foo.aux foo.log
If the file foo.pdf
does not exist, or exists but it older than foo.tex
, the command make foo.pdf
will create the PDF file by running pdflatex
. If foo.pdf
is newer than foo.tex
then running make foo.pdf
will return a message
make: 'foo.pdf' is up to date.
If you run make
with no argument, it will build both foo.pdf
and foo.docx
as needed.
The command make clean
deletes auxiliary files created by pdflatex
. This shows that a make
action does have to “make” anything; it simply runs a command.
[1] The blog post title is an allusion to the 1964 movie Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb.
[2] I use LaTeX for everything I can. This saves time in the long run, if not in the moment, because of consistency. I used to use Word for proposals and such, and LaTeX for documents requiring equations. My workflow became more efficient when I switched to using LaTeX for everything.
I think your foo.docx target should depend on foo.tex, rather than foo.pdf.
I love using make for all sorts of tasks, including document builds. In addition to the benefits noted in reference [1] above (e.g. dependency management & parallel task dispatch), I would include: automatic iteration over multiple targets & exit if a non-zero status is returned (unless the rule is preceded with “-” or (GNU) make invoked with “-i”). The latter is very handy if you want to include successful execution of an (e.g. awk) data validation script on an input file as a dependency, or to check you are on the master repository branch with a clean working directory before deploying files, or example. The manual at https://www.gnu.org/software/make/manual/ is well work a read.
If you want all *.tex files converted without having to hardcode them, you can do something like this: https://github.com/brunns/cheatsheets/blob/master/Makefile#L7
If you are already using python in your project, try https://pydoit.org/ instead of make files.