Вторичное расширение в Makefile приводит к запуску ненужных целей

Я пытаюсь написать Makefile, который создает выходные данные PDF с помощью LaTeX, используя Latexmk. Выходные данные имеют в основном одно и то же правило с разными предварительными условиями, поэтому я попытался обобщить свой исходный Makefile, используя вторичное расширение GNU Make. (Я также создал .PHONY цели, также с дополнительным расширением, чтобы сделать их более удобными для пользователя.) Однако это приводит к тому, что обязательные правила всегда выполняются, даже если в них нет необходимости. . К счастью, Latexmk достаточно умен, чтобы избежать ненужной работы, но мне интересно, делаю ли я что-то не так...

Чтобы попытаться абстрагироваться от того, что я пытаюсь:

      ,-> foo -> build/foo.pdf
all -{
      `-> bar -> build/bar.pdf

То есть цель all строит foo и bar. Эти цели открывают соответствующий PDF-файл, для которого предварительное условие равно build/X.pdf (где X равно foo или bar). Это подлинные цели, которые создают соответствующий файл PDF.

Вот что я придумал:

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)/[email protected]
    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

Просто для ясности: правило для build/X.pdf должно выполняться всякий раз, когда файлы, перечисленные в XSRC (опять же, где X — это foo или bar), новее, чем PDF-файлы, или PDF-файлы не существуют; но он не должен работать иначе.


person Xophmeister    schedule 07.07.2021    source источник


Ответы (1)


Я считаю, что это стало несколько сложным, больше, чем должно быть. Часть этих вторых операторов расширения можно просто заменить правилами статического шаблона. . Другое дело, что .SECONDEXPANSION: делает все дальнейшее Makefile содержимое подчиненным второе расширение, так что вам не нужно явно указывать его перед каждой целью (было бы гораздо понятнее пометить таким образом .PHONY целей, чтобы быстро увидеть, является ли цель фальшивой или нет).

Тем не менее, я считаю, что наиболее важной проблемой здесь является то, что вы упомянули каталог в качестве предварительного условия. Помните, что make решает, следует ли перестраивать цель, на основе временной метки зависимостей, а временная метка каталога всегда обновляется всякий раз, когда обновляется файл в этом каталоге. Поэтому всякий раз, когда вы пишете $(BUILD_DIR)/foo.pdf, временная метка $(BUILD_DIR) обновляется, и следующий вызов будет построен снова, поскольку каталог новее. Этого можно избежать, указав каталог в качестве требования только для заказа. (что означает: построить, если он не существует, но не проверять метку времени).

Собрав все вместе, я бы сделал это так:

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)

Выход:

$ 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

Обратите внимание, что последующий вызов не пытался ничего построить, просто откройте файл PDF. Однако он по-прежнему реагирует на изменение файла:

$ 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
person raspy    schedule 07.07.2021
comment
Спасибо :) Помимо предварительных требований только для заказа, я никогда не видел правила, подобного $(TARGETS): %: $(BUILD_DIR)/%.pdf, с дополнительным битом между целью и предварительными условиями. Как это называется, чтобы я мог прочитать об этом в руководстве? - person Xophmeister; 08.07.2021
comment
Нашел: «Правила статического паттерна» - person Xophmeister; 08.07.2021
comment
Рад, что ты нашел это. Я связал это в ответе. - person raspy; 08.07.2021