makefile

Makefile seperate wildcald rule with more dependencies for files in a specific directory?


Say we have a project where using make, we create .bar files, then combine them into a foobar file for the final result. We make each .bar file from a .foo file, and in the foo directory we also use a .foobar file which we also create using make.

I came up with this Makefile to do this:

default: all

%.bar: %.foo
        @echo generic build for $@
        @cat $< > $@

foo/%.bar: foo/%.foo foo/%.foobar
        @echo specific build for $@
        @cat $< > $@

%.foobar:
        @echo building $@
        @echo foobar > $@

foobar: foo/foo.bar bar/foo.bar
        @echo building $@
        @cat foo/foo.bar bar/foo.bar > $@

all: foobar

clean:
        rm -r foobar */*.bar

.PHONY: all clean

Then I set up an environment like this:

mkdir foo bar
touch foo/foo.foo bar/foo.foo

Now, running make I expect foo/foo.bar to be built using the second rule, and bar/foo.bar using the first. Instead, I see the following output:

generic build for foo/foo.bar
generic build for bar/foo.bar
building foobar

Indicating that for both files, the first rule was used.

Why does this happen and how do I fix it? It seems to be related to having dependencies that don't exist yet compared to the other rule, since if I do a make foo/foo.foobar first then run make the second rule gets used for foo/foo.bar. I want everything in the foo directory to specifically use the second rule with these dependencies, never the first one.


Solution

  • Why does this happen and how do I fix it?

    Section 10.5.4 of the GNU make manual explains:

    a[n implicit] rule which can be satisfied without chaining other implicit rules (for example, one which has no prerequisites or its prerequisites already exist or are mentioned) always takes priority over a rule with prerequisites that must be made by chaining other implicit rules.

    Pattern rules are implicit rules, and that is the effect you are observing. To overcome it, you can either avoid using a pattern rule for building .foobar files, at least in folder foo/, or remove the .foobar files from the prerequisite list for .bar targets in that folder. Among the possible alternatives are:

    • write a regular rule for building those .foobar files. This will require designating all of them explicitly, but is not otherwise much different from what you're already doing. For example,

      FOO_FOOBARS = foo/foo.foobar
      $(FOO_FOOBARS):
               @echo building $@
               @echo foobar > $@
      

      OR

    • Remove foo/%.foobar from the prerequisite list of foo/%.bar. Instead, use recursive make in that recipe to build the .foobar file when needed, something like this:

      foo/%.bar: foo/%.foo
              $(MAKE) $(<)bar
              @echo specific build for $@
              @cat $< > $@
      

    Of those, I prefer the former.