ovmjm
ovmjm

Reputation: 1696

Pattern rule not found in Makefile

Let's say I have this minimalist main.c program

int main(void)
{
    return 0;
}

and this Makefile

.PHONY: %.run

%.run: %
    ./$<

If I run the following command

make main.run

which, I expect, would

  1. make the main executable from main.c (using make default implicit %: %.c rule)
  2. execute main
  3. exit succesfully.

But what I get instead is the following error

make: *** No rule to make target 'main.run'.  Stop.

I tried to disable default implicit rules and set my own compile rule with

.SUFFIXES:

%: %.c
    $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $<

but this doesn't help.

However, in both cases, if I run make main first, then make main.run, it is working as expected. But running two commands is exactly what I am trying to avoid with my %.run rule.

Of course if I replace my pattern rule with this explicit rule

main.run: main
    ./main

it is working.

Also, if I change my pattern rules to %.x: %.c and %.run: %.x with a .x suffix for the executables, this is working, but again I don't want suffixes.

Of course, my question is not to know if this is the right way to compile and run a program in one command, it is really to know why this Makefile does not execute the way I am expecting.

Upvotes: 4

Views: 1425

Answers (1)

Joseph Quinsey
Joseph Quinsey

Reputation: 9962

It would appear the restrictions of the Match Anything Rules for targets also apply to prerequisites, although I can not find this stated anywhere1.

If the executable main already exists, the OP's makefile:

%.run: %
    ./$<

works perfectly fine. Two ways to fix the problem are to:

  • Add an explicit rule to build main, or
  • Add an empty rule with target main; this will cause main to be built by the implicit pattern-matching rules, or an explicit rule.

We give four solutions.

1. The following makefile works (tested on GNU make):

%.run: %
    ./$<

main:

Here the word main is hard-coded in the makefile. You could handle adhoc targets via:

RUN_TARGETS = main.run foo.run bar.run $(filter %.run,$(MAKECMDGOALS))

%.run: %
    ./$<

$(RUN_TARGETS:.run=) dummy:

(We need filter to ensure that a typo such as make claen returns an error message. dummy is used to avoid an empty target list.)

2. Another variation is to use a Static Pattern Rule, which seems to bypass this bug/feature:

RUN_TARGETS = main.run foo.run bar.run $(filter %.run,$(MAKECMDGOALS))

$(RUN_TARGETS) dummy.run: %.run: %
    ./$<

3. And yet another way is to use a terminal match-anything rule, if all your executables can be built from a single C file, as per the built-in rules. This also deletes main after running, for whatever reason:

%.run: %
    ./$<

%:: %.c  # note double-colon, for terminal match-anything rule
    cc $< -o $@

4. Finally, a completely different way to resolve the problem is to handle the prerequisite explicitly by invoking make recursively:

%.run:
    make $*
    ./$*

For somewhat-related questions, see GNU make seems to ignore non-terminal match-anything rules for intermediate files and Force make to use a more specific rule. Note that putting main as prerequisite to an unrelated rule, such as:

%.run: %
    ./$<

garbage: main

does not seem to work.

1 But see perhaps Makefile match anything rule as intermediate.

Upvotes: 1

Related Questions