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

Я должен интегрировать генерацию многих файлов HTML в существующий Makefile, Проблема в том, что файлы HTML должны находиться во многих разных каталогах. Моя идея состоит в том, чтобы написать неявное правило, которое преобразует исходный файл (*.st) в соответствующий HTML-файл

%.html: %.st
    $(HPC) -o $@ $<

и правило, которое зависит от всех HTML-файлов

all: $(html)

Если файл HTML отсутствует в builddir make не находит неявное правило: *** No rule to make target, Если я изменю неявное правило так

$(rootdir)/build/doc/2009/06/01/%.html: %.st  
    $(HPC) -o $@ $<

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

  1. Разделите t на часть каталога, называемую d, а остальные - n. Например, если т src/foo.o', then d issrc/'и n это `foo.o'.
  2. Составьте список всех шаблонных правил, одно из которых соответствует целям t или n. Если целевой шаблон содержит косую черту, он сопоставляется с t; в противном случае против п.

Почему неявное правило не найдено, и что было бы наиболее элегантным решением, если принять 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)

4 ответа

Решение

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

Наилучшее решение, которое я нашел до сих пор, - это генерировать неявное правило для целевого каталога с помощью 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)

Ваше активное неявное правило делает $(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 в некоторой степени стандартный способ работы с подкаталогами, но остерегайтесь последствий, упомянутых в статье "Рекурсивное использование считается вредным"

Другие вопросы по тегам