tags:

views:

276

answers:

2

I have a Makefile with the following type of rule:

%.html:
    ./generate-images.py > $@
    make $(patsubst %.png,%.gif,$(wildcard *.png))

The generate-images script writes not only the HTML file (to stdout) but several .png files to the current directory. The goal here is to convert them to .gif. (not really, but this is an example)

This works if I invoke it directly. The problem is: If I invoke it from another rule where foo.html is a dependency, the wildcard statement fails to find any files. In other words, it just called make with no arguments, which is not what I want here.

What's the deal with the wildcard? Or, is there a better way to do this?

+2  A: 

That sounds like it's evaluating all of the $() expressions as it's processing the Makefile, rather than as it executes each rule. You could add a rule to your makefile like so:

images: $(patsubst %.png,%.gif,$(wildcard *.png))
.PHONY: images

and then change your example snippet to

%.html:
    ./generate-images.py > $@
    make images

so that Make evaluates the glob at the right time. This is something about which checking the manual might be worthwhile.

Novelocrat
+3  A: 

While your problem may be something different, I clearly see one.

The whole text of all commands within the rule is simultaneously processed so that make's functions and variables get expanded. Assume you have no .png files in the directory, and you invoke make so it should regenerate them: a.png and b.png. Then, after you invoke make, the text of the rule would effectively look like this:

file.html:
    ./generate-images.py > file.html
    make

because at the moment of reading the makefile there were no .png files! After the first line is executed, the files will appear, but the next line was already generated to be just "make".

And only when you invoke your makefile for the second time, will it expand to

file.html:
    ./generate-images.py > file.html
    make a.gif b.gif


This is not what you want. So I suggest doing it in The Right Way.

# If you have batch conversion program, this may be helpful
images.stamp: *.png
     convert_all_images $?
     touch images.stamp

# OR, if you want convert one-by-one with means of make
images.stamp: $(wildcard *.png)
    touch images.stamp

%.gif: %.png
    convert_one --from=$^ --to=$@

# HTML would look like
%.html:
     ./generate-images.py > $@
     make images.stamp

So when you invoke make all, it generates htmls and converts newly generated images. Note that it will only convert the images that are updated, which is what you want.


Thanks to Beta for pointing out the mess with gif/png extensions.

Pavel Shved
This is what my post was getting at.
Novelocrat
+1 for a clear explanation of the wildcard problem, -1 for overcomplicated and buggy rules (even after editing), and +1 for a surprising new insight: you really can't do this without recursion!
Beta
@Beta: I'd really want to know what bugs left there in my rules! Please, drop a hint--I'll edit my answer and give you proper attribution.BTW, actually, you *can* do it without recursion :-) That's why I've removed that claim from my post :-) But that woul make my post even more coplicated...
Pavel Shved
Your %.png:%.gif rule should be the other way around, and your images.stamp rule will have no preqs (and make no gifs) until someone has made the gifs another way. Now how can you do it without recursion (and without, e.g., putting in a huge Perl script that's basically a stripped-down implementation of make)? If there's a way then you've surprised me twice in a row!
Beta
@Beta: instead of recursion, we can use the ability to reload makefiles. If we write target `Makefile: htmls // touch Makefile`, after processing htmls `make` will re-read makefile, since the it was updated. And it will then also re-expand our wildcards.
Pavel Shved
Oh come on, that's just an inelegant way to have Make invoke Make, which is what recursion is. It doesn't meet the manual's definition of recursion because "make" doesn't appear in a command, but that's victory by razor-thin technicality.
Beta
@Beta: it's not a recursion, it's a loop. When you recurse to another make subprocess, you still can do something after it finishes. If you re-read makefile, you can't return. That's why I avoid calling it recursion.
Pavel Shved