I am trying to write a Makefile that builds PDF outputs with LaTeX, using Latexmk. The outputs have basically the same rule, with different prerequisites, so I tried generalising my original Makefile using GNU Make's "secondary expansion". (I also created .PHONY
targets, also with secondary expansion, to make it more user-friendly.) However, this is causing the prerequisite rules to always be run, even when they don't need to be. Fortunately, Latexmk is clever enough to avoid doing unnecessary work, but I wonder if I'm doing anything wrong...
To try to abstract what I'm attempting:
,-> foo -> build/foo.pdf
all -{
`-> bar -> build/bar.pdf
That is, the all
target builds foo
and bar
. These targets open the respective PDF file, which have a prerequisite of build/X.pdf
(where X
is foo
or bar
). These are genuine targets which build the appropriate PDF file.
This is what I've come up with:
TARGETS = foo bar
BUILD_DIR = build
OUTPUTS = $(TARGETS:%=$(BUILD_DIR)/%.pdf)
commonSRC = src/preamble.tex src/header.tex # etc...
fooSRC = src/foo.tex $(commonSRC) # etc...
barSRC = src/bar.tex $(commonSRC) # etc...
all: $(TARGETS)
.SECONDEXPANSION:
$(TARGETS): $(BUILD_DIR)/$$@.pdf
open $<
# FIXME This isn't quite right: This rule is still getting called by the
# above rule, even when it doesn't need to be. Latexmk is clever enough
# not to do any extra work, but it shouldn't run at all.
.SECONDEXPANSION:
$(OUTPUTS): $$($$(subst .pdf,SRC,$$(@F))) $(BUILD_DIR)
latexmk -outdir=$(BUILD_DIR) -auxdir=$(BUILD_DIR) -pdf $<
$(BUILD_DIR):
mkdir $@
clean:
rm -rf $(BUILD_DIR)
.PHONY: all $(TARGETS) clean
Just to be clear: The rule for build/X.pdf
should run whenever the files enumerated in XSRC
(again, where X
is foo
or bar
) are newer than the PDFs, or the PDFs don't exist; but it should not run otherwise.
I believe that this got somewhat complex, more than it needs to be. Part of these second expansion statements can be just replaced with static pattern rules. The other thing is that .SECONDEXPANSION:
makes all further Makefile
contents to be subject to second expansion, so you don't need to explicitly state it before every target (it would be much clearer to mark .PHONY
targets this way to quickly see if a target is phony or not).
Nevertheless, I believe that most important issue here is that you mention a directory as a prerequisite. Remember that make
decides on whether to rebuild the target based on dependencies timestamp, and a directory gets its timestamp always updated whenever a file in this directory is updated. Therefore, whenever you write $(BUILD_DIR)/foo.pdf
, $(BUILD_DIR)
timestamp gets updated and the next call will build again since the directory is newer. You can avoid it by specifying a directory as an order-only prerequisite (which means: build if it doesn't exist, but do not check timestamp).
Putting it all together I would make it this way:
TARGETS = foo bar
BUILD_DIR = build
commonSRC = src/preamble.tex src/header.tex # etc...
fooSRC = src/foo.tex $(commonSRC) # etc...
barSRC = src/bar.tex $(commonSRC) # etc...
.SECONDEXPANSION:
.PHONY: all
all: $(TARGETS)
.PHONY: $(TARGETS)
$(TARGETS): %: $(BUILD_DIR)/%.pdf
echo open $<
$(BUILD_DIR)/%.pdf: $$($$*SRC) | $(BUILD_DIR)
echo latexmk -outdir=$(BUILD_DIR) -auxdir=$(BUILD_DIR) -pdf $< > $@
$(BUILD_DIR):
mkdir -p $@
.PHONY: clean
clean:
rm -rf $(BUILD_DIR)
Output:
$ make all
mkdir -p build
echo latexmk -outdir=build -auxdir=build -pdf src/foo.tex > build/foo.pdf
echo open build/foo.pdf
open build/foo.pdf
echo latexmk -outdir=build -auxdir=build -pdf src/bar.tex > build/bar.pdf
echo open build/bar.pdf
open build/bar.pdf
$ make all
echo open build/foo.pdf
open build/foo.pdf
echo open build/bar.pdf
open build/bar.pdf
Note that subsequent call did not attempt to build anything, just open the pdf. It still reacts on the file change however:
$ touch src/foo.tex
$ make all
echo latexmk -outdir=build -auxdir=build -pdf src/foo.tex > build/foo.pdf
echo open build/foo.pdf
open build/foo.pdf
echo open build/bar.pdf
open build/bar.pdf
$ touch src/header.tex
$ make all
echo latexmk -outdir=build -auxdir=build -pdf src/foo.tex > build/foo.pdf
echo open build/foo.pdf
open build/foo.pdf
echo latexmk -outdir=build -auxdir=build -pdf src/bar.tex > build/bar.pdf
echo open build/bar.pdf
open build/bar.pdf