GNU make со многими целевыми каталогами

Мне нужно интегрировать создание многих файлов 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 $@ $<

он найден, но тогда мне нужно иметь неявное правило почти для каждого файла в проекте. В соответствии с алгоритмом поиска неявных правил в GNU make, поиск по правилам работает так:

  1. Разделите все имя цели t на часть каталога с именем d и оставшуюся часть с именем n. Например, если t равно src/foo.o, то d равно src/, а n равно foo.o.
  2. Составьте список всех шаблонных правил, одна из целей которых соответствует 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)

person Ludwig Weinzierl    schedule 01.06.2009    source источник


Ответы (4)


Как и Мария Шальнова, мне нравится рекурсивный make (хотя я не согласен с "Рекурсивный Make считается вредным"), и вообще лучше делать что-то ЗДЕСЬ из источника ТАМ, а не наоборот. Но если вам нужно, я предлагаю небольшое улучшение: сгенерируйте generateHtml только ПРАВИЛО, а не КОМАНДЫ.

person Community    schedule 19.06.2009

Лучшее решение, которое я нашел до сих пор, — создать неявное правило для каждого целевого каталога с помощью foreach-eval-call, как описано в руководстве GNU 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)
person Ludwig Weinzierl    schedule 01.06.2009
comment
Я не вижу другого решения, кроме использования какой-либо формы call-foreach-eval. GNU make плохо справляется с такой структурой каталогов. (На самом деле исходная версия более или менее предполагала, что весь исходный код находится в одном каталоге.) - person JesperE; 01.06.2009

Ваше активное неявное правило делает $(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))))
person Douglas Leeder    schedule 01.06.2009
comment
Это сработает, но, к сожалению, исходный макет исправлен, а настоящий Makefile использует VPATH. - person Ludwig Weinzierl; 01.06.2009

Другая возможность состоит в том, чтобы коммандос make вызывал себя рекурсивно с аргументом -C для каждого выходного каталога. Рекурсивный make - это в некотором роде стандартный способ работы с подкаталогами, но остерегайтесь последствий, упомянутых в статье "Рекурсивный make считается вредным"

person msh    schedule 01.06.2009
comment
Хорошо, рекурсивный make не пришел мне в голову. Ваше решение будет достаточно портативным. - person Ludwig Weinzierl; 01.06.2009