I have to integrate the generation of many HTML files in an existing Makefile
.
The problem is that the HTML files need to reside in many different directories.
My idea is to write an implicit rule that converts the source file (*.st) to the corresponding html file
%.html: %.st
$(HPC) -o $@ $<
and a rule that depends on all html files
all: $(html)
If the HTML file is not in the builddir make
doesn't find the implicit rule: *** No rule to make target
.
If I change the implicit rule like so
$(rootdir)/build/doc/2009/06/01/%.html: %.st
$(HPC) -o $@ $<
it's found, but then I have to have an implicit rule for nearly every file in the project.
According to Implicit Rule Search Algorithm in the GNU make
manual, rule search works like this:
- Split t into a directory part, called d, and the rest, called n. For
example, if t is
src/foo.o', then d
is
src/' and n is `foo.o'.
- Make a list of all the pattern rules one of whose targets matches t or n.
If the target pattern contains a
slash, it is matched against t;
otherwise, against n.
Why is the implicit rule not found, and what would be the most elegant solution, assuming GNU make
is used?
Here is a stripped down version of my 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)
Like Maria Shalnova I like recursive make (though I disagree with "Recursive Make Considered Harmful"), and in general it's better to make something HERE from a source THERE, not the reverse. But if you must, I suggest a slight improvement: have generateHtml generate only the RULE, not the COMMANDS.
The best solution I found so far is to generate an implicit rule per target directory via foreach-eval-call, as explained in the GNU make
manual. I have no idea how this scales to a few thousand target directories, but we will see...
If you have a better solution, please post it!
Here is the code:
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)
Your active implicit rule makes $(rootdir)/build/doc/2009/06/01/some.html
depend on $(rootdir)/build/doc/2009/06/01/some.st
. If $(rootdir)/build/doc/2009/06/01/some.st
doesn't exist then the rule won't be used/found.
The commented out rule makes $(rootdir)/build/doc/2009/06/01/some.html
depend on some.st
.
One solution is to make you're source layout match your destination/result layout.
Another option is to create the rules as required with eval
. But that will be quite complicated:
define HTML_template
$(1) : $(basename $(1))
cp $< $@
endef
$(foreach htmlfile,$(html),$(eval $(call HTML_template,$(htmlfile))))
An other possibility is to have the commando make
call itself recursively with the argument -C with every output directory.
Recursive make
is somewhat the standard way to deal with subdirectories, but beware of the implications mentioned in the article "Recursive Make Considered Harmful"