views:

781

answers:

4

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:

  1. 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'.
  2. 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)
+3  A: 

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))))
Douglas Leeder
That would work, but unfortunately the source layout is fixed and the real Makefile uses VPATH.
Ludwig Weinzierl
+3  A: 

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)
Ludwig Weinzierl
I can't see any other solution than using some form of call-foreach-eval. GNU make does not cope well with this kind of directory layouts. (In fact, the original make more or less assumed that you had all your source code in a single directory.)
JesperE
+3  A: 

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"

Maria Shalnova
OK, recursive make didn't come to my mind. Your solution would be quite portable.
Ludwig Weinzierl
+4  A: 

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.