Search code examples
makefilegnu-make

How can I tell GNU Make that one source file produces multiple outputs?


I have a program which takes an input foo.svg and produces some number of outputs: foo.0001.png, foo.0002.png, etc. The number of these is deterministic, but not necessarily known at the time the Makefile is written.

I can't find out how to write a Makefile rule for %.svg whose effect is

  • if there are no $(basename $<).*.png in the current directory, run the command
  • if there are any such, and any of them are older than $<, run the command
  • otherwise do nothing.

I feel I must be missing something obvious. Can you throw me a clue?


Solution

  • The important thing to understand about make is that it doesn't start with the source files and build the output. It starts with the output, and tries to figure out how that output might be built from files that are available and whether the output is out of date.

    So, if you don't know for sure what output you need, for example if the generated output is not deterministic, it's tricky to get make to build things because it's working backward from the final result.

    What I would recommend is using a "sentinel file" with a known name, to determine the last time the recipe was invoked. You can add any other outputs as well to get rebuilds to happen.

    The second important thing to realize is that automatic variables like $< etc. are only defined inside the recipe. You can't use them outside the recipe (for example in the prerequisites list). So putting something like $(basename $<).*.png into the prerequisites list cannot work because $< is empty here.

    Putting it all together, maybe something like this will work:

    SOURCES := $(wildcard *.svg)
    
    SENTINELS := $(SOURCES:%.svg=.sentinel.%)
    
    all: $(SENTINELS)
    
    .sentinel.% : %.svg
            recipe to process $<
            @touch $@
    

    (note, untested)