makefilegnu-make

Makefile not building pattern rule prerequisites when it involves a chain


Consider this Makefile:

run-%: %
    ./$<

I have a test1.cpp file, so I expect make to build test1.o and test1 using chained implicit rules when I run make run-test1. However, what I get is

make: *** No rule to make target 'run-test1'.  Stop.

I can fix this issue by adding a blank target test1: in the Makefile. What's going on?


Solution

  • I expect make to build test1.o and test1 using chained implicit rules when I run make run-test1.

    make wouldn't do exactly that in any case. If you run make test1 with test1.cpp present and no makefile at all, the rule it will use builds the executable directly from the source, with no intermediate object file:

    %: %.cpp
    #  recipe to execute (built-in):
            $(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@
    

    Of course, you don't see that rule being engaged either in your case. It is a non-terminal match-anything rule, as is the rule for %: %.o, and per the manual,

    for performance reasons make will not consider non-terminal match-anything rules (i.e., ‘%:’) when searching for a rule to build a prerequisite of an implicit rule (see Match-Anything Pattern Rules).

    That is, make will not do what you want, by design.

    I can fix this issue by adding a blank target test1: in the Makefile.

    I take you to mean you add a rule for target test1 with no prerequisites or recipe. When I do that, I reproduce the behavior you describe, but it does not seem to be consistent with make's documentation:

    If a rule has no prerequisites or recipe, and the target of the rule is a nonexistent file, then make imagines this target to have been updated whenever its rule is run. This implies that all targets depending on this one will always have their recipe run.

    In this case, however, my make and yours seem to be instead exercising the standard behavior for other cases where a file is named as a target of a rule, but no such rule has a recipe (but they do have prerequisites), which is to build it according to an implicit rule. I don't think this is intentional.

    Since that doesn't anyway get you the generalized build-anything-and-run-it behavior you seem to want, it's probably not worth studying any more deeply. You can get that general behavior by adding an implicit rule mirroring the built-in one, but which isn't a match-anything rule. For example:

    run-%: %.x
        ./$<
    
    %.x: %.cpp
        $(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@
    

    Demo:

    $ make run-test1
    g++     test1.cpp   -o test1.x
    ./test1.x
    rm test1.x
    

    You will note that make automatically deletes the executable after running it. That is its normal behavior for intermediate files in chains of implicit rules.