Search code examples
makefilegnu-make

Makefile to convert specific files in one directory structure into mirrored structure in another directory


I have %.foo files that live in an $(A) directory with additional year/month substructure, eg $(A)/2014/05/x.foo etc. With a Makefile, which I want to keep in the directory where the ./converter lives, I want to convert all the %.foo files in directories $(A)/*/*/ into a mirrored $(B)/*/*/ structure. Additional complication is that the converter unfortunately converts a %.foo file into a file with two extensions, ie %.bar.baz and this behavior cannot be changed. How should I correctly construct the dependencies of the $(B) files on $(A) files? How should I write the %.bar.baz: %.foo rule?

The problem is similar to this question. I tried with

AF := $(shell find $(A) -name \*.foo)
BF := $(patsubst $(A)/%.foo,$(B)/%.bar.baz,$(AF))

all: $(BF)
.PHONY: all

$(B)/%.bar.baz: %.foo
        mkdir -p $(@D)
        ./convert $< $@ || rm $@

but with make or gmake I just get

make: *** No rule to make target 'b/2014/05/x.bar.baz', needed by 'all'.  Stop.

Solution

  • This rule is wrong:

    $(B)/%.bar.baz: %.foo
            mkdir -p $(@D)
            ./convert $< $@ || rm $@
    

    Let's see what make will do with the target you want to build, b/2014/05/x.bar.baz. Make will look for a target pattern that matches this, and it finds $(B)/%.bar.baz (because $(B) expands to b), so the pattern is really b/%.bar.baz.

    Given this pattern, what is the stem (the part that matches the %)? It will be 2014/05/x.

    Given that stem, what is the prerequisite that make looks for when the prerequisite pattern is %.foo? We replace the stem and we get 2014/05/x.foo.

    So make looks for the file 2014/05/x.foo, but that file doesn't exist. The file that exists is a/2014/05/x.foo (I will assume: you don't actually show us the value of the A or B variables). Since the prerequisite file doesn't exist, and make can't find a way to build it, make decides this pattern rule doesn't match. Since this pattern doesn't match make will look for some other pattern rule that will match. Since no other pattern rule matches either, make tells you it doesn't know how to build this target b/2014/05/x.bar.baz.

    So the simple answer is, don't forget to add the directory $(A) to the prerequisite:

    $(B)/%.bar.baz: $(A)/%.foo
            mkdir -p $(@D)
            ./convert $< $@ || rm $@