2
votes

Consider the following Makefile:

%.o.gz: %.o
    gzip $<

If hello.c exists in the same directory, I can invoke make with the target hello.o.gz:

$ make hello.o.gz
cc    -c -o hello.o hello.c
gzip hello.o
$ 

I believe this invokes a chain of implicit rules:

  • built-in implicit rule to compile .c to .o
  • implicit rule given in the makefile to gzip the .o file

This works fine as I would expect it to.

Now suppose I modify the makefile a bit:

%.gz: %
    gzip $<

The idea here is to use the "compile .c to binary" built-in implicit rule, then gzip the resulting binary. I expect to be able to invoke make like this:

$ make hello.gz
make: *** No rule to make target `hello.gz'.  Stop.
$

But it doesn't work.

Interestingly it does work if I invoke make twice, once to build the binary, and once to gzip it, so this appears to have something to do with the rule chaining:

$ make hello
cc     hello.c   -o hello
$ make hello.gz
gzip hello
$ 

Why can I not put the "compile .c to binary" built-in implicit rule into a chain whereas I can put a "compile .c to .o" implicit rule into a chain?

Is there some way I can modify my implicit gzip rule to make this work?

I have read the Chains of Implicit Rules section of the make documentation, but as far as I can see there is no exception for the "compile .c to binary" built-in implicit rule. Or am I missing something?

2

2 Answers

2
votes

I believe you're hitting this behvaior because you're using match-anything rules (that is, a rule where the target is a % with no restriction). There are a number of special cases for handling these rules, because without those special cases the number of rules make would need to check during implicit rule search would be many orders of magnitude larger.

See http://www.gnu.org/software/make/manual/html_node/Match_002dAnything-Rules.html for information on the restrictions placed on match-anything rules.

0
votes

The best workaround I could come up with so far is:

ifneq (,$(wildcard $(MAKECMDGOALS:%.gz=%.c)))
$(MAKECMDGOALS): %.gz: %
    gzip $<
endif

This falls apart if more than one target is passed to make.