GNU make со многими целевыми каталогами
-
06-09-2019 - |
Вопрос
Мне нужно интегрировать создание множества HTML-файлов в существующий Makefile
.Проблема в том, что файлы HTML должны находиться во многих разных каталогах.Моя идея состоит в том, чтобы написать неявное правило, которое преобразует исходный файл (*.st) в соответствующий html-файл.
%.html: %.st
$(HPC) -o $@ $<
и правило, которое зависит от всех html-файлов
all: $(html)
Если HTML-файл отсутствует в каталоге сборки make
не находит неявное правило: *** No rule to make target
.Если я изменю неявное правило вот так
$(rootdir)/build/doc/2009/06/01/%.html: %.st
$(HPC) -o $@ $<
оно найдено, но тогда мне нужно иметь неявное правило почти для каждого файла в проекте.В соответствии с Алгоритм неявного поиска правил в ГНУ make
вручную, поиск по правилам работает следующим образом:
- Разделите t на часть каталога с именем d и остальную часть с именем n.Например, если T
src/foo.o', then d is
src/», а n — «foo.o».- Составьте список всех шаблонных правил, одна из целей которых соответствует t или n.Если целевой шаблон содержит черту, он сочетается с T;в противном случае, против n.
Почему неявное правило не найдено и какое решение было бы наиболее элегантным, если предположить, что GNU make
используется?
Вот урезанная версия моего Makefile
:
rootdir = /home/user/project/doc
HPC = /usr/local/bin/hpc
html = $(rootdir)/build/doc/2009/06/01/some.html
%.html: %.st
$(HPC) -o $@ $<
#This works, but requires a rule for every output dir
#$(rootdir)/build/doc/2009/06/01/%.html: %.st
# $(HPC) -o $@ $<
.PHONY: all
all: $(html)
Решение
Как и Мария Шальнова, мне нравится рекурсивный make (хотя я не согласен с «Рекурсивный Make считается вредным»), и вообще лучше сделать что-то ЗДЕСЬ из источника ТАМ, а не наоборот.Но если вам необходимо, я предлагаю небольшое улучшение:заставитьgenerHtml генерировать только ПРАВИЛО, а не КОМАНДЫ.
Другие советы
Лучшее решение, которое я нашел на данный момент, — это создать неявное правило для каждого целевого каталога через foreach-eval-вызов, как поясняется в ГНУ make
руководство.Я понятия не имею, как это масштабируется до нескольких тысяч целевых каталогов, но посмотрим...
Если у вас есть лучшее решение, пожалуйста, опубликуйте его!
Вот код:
rootdir = /home/user/project/doc
HPC = /usr/local/bin/hpc
html = $(rootdir)/build/doc/2009/06/01/some.html \
$(rootdir)/build/doc/2009/06/02/some.html
targetdirs = $(rootdir)/build/doc/2009/06/01 \
$(rootdir)/build/doc/2009/06/02
define generateHtml
$(1)/%.html: %.st
-mkdir -p $(1)
$(HPC) -o $$@ $$<
endef
$(foreach targetdir, $(targetdirs), $(eval $(call generateHtml, $(targetdir))))
.PHONY: all
all: $(html)
Ваше активное неявное правило делает $(rootdir)/build/doc/2009/06/01/some.html
зависит от $(rootdir)/build/doc/2009/06/01/some.st
.Если $(rootdir)/build/doc/2009/06/01/some.st
не существует, то правило не будет использовано/найдено.
Закомментированное правило делает $(rootdir)/build/doc/2009/06/01/some.html
зависит от some.st
.
Одним из решений является соответствие исходного макета макету назначения/результата.
Другой вариант — создать необходимые правила с помощью eval
.Но это будет довольно сложно:
define HTML_template
$(1) : $(basename $(1))
cp $< $@
endef
$(foreach htmlfile,$(html),$(eval $(call HTML_template,$(htmlfile))))
Другая возможность — использовать коммандос. make
рекурсивно вызывать себя с аргументом -C для каждого выходного каталога.Рекурсивный make
это в некоторой степени стандартный способ работы с подкаталогами, но остерегайтесь последствий, упомянутых в статье «Рекурсивное использование считается вредным».