Search code examples
makefilegnu-make

Pattern match for files in multiple subdirectories where the names of the subdirectories is unknown


I am trying to create a makefile pattern to transform the source files articles/*/source.txt to target files articles/*/target.html, where * is one and/or multiple single-level directories with unknown names.

Each source.txt file should when running make generate a target.html file in the same corresponding directory.

I was trying to use $(wildcard article/*) to create a list of all directories i was targeting, however it seems to not be able to combine that list with a file fragment string (like $(SUBDIRS)/target.html).

.PHONY: build

SUBDIRS := $(wildcard article/*)

build: $(SUBDIRS)/target.html

$(SUBDIRS)/target.html: $(SUBDIRS)/source.txt
    echo "Generate html..." > $(SUBDIRS)/target.html

Solution

  • This cannot work:

    build: $(SUBDIRS)/target.html
    

    Suppose SUBDIRS is article/foo article/bar article/baz. What will the above expand to? There's no magical force in make that will see that a variable expands to multiple words, and that there's a postfix on the variable, and append it to every word individually. Instead, the above simply expands to the variable value followed by /target.html:

    build: article/foo article/bar article/baz/target.html
    

    which is clearly not what you want. Ditto for the the rule.

    You want this:

    build: $(addsuffix /target.html,$(SUBDIRS))
    

    (or you could use patsubst instead if you prefer).

    When writing the rule:

    $(SUBDIRS)/target.html: $(SUBDIRS)/source.txt
    

    you have the same problem, but the above solution still won't give you what you want. If you use the addsuffix here you get this:

    article/foo/target.html article/bar/target.html article/baz/target.html: article/foo/source.txt article/bar/source.txt article/baz/source.txt
    

    which will sort of work but it means the same thing as this:

    article/foo/target.html: article/foo/source.txt article/bar/source.txt article/baz/source.txt
    article/bar/target.html: article/foo/source.txt article/bar/source.txt article/baz/source.txt
    article/baz/target.html: article/foo/source.txt article/bar/source.txt article/baz/source.txt
    

    so in other words, every target file depends on ALL the source files so if any source file changes, all the targets are recreated which is probably not what you want.

    As alluded to by HolyBlackCat, you want a pattern rule which gives a template for how to build one target:

    article/%/target.html: article/%/source.txt
    

    make will apply this pattern rule to build each individual target.