Pattern rule depending on another pattern rule - makefile

consider the following fragment of my makefile:
gui_backend: $(BUILDDIR)/gui_backend
$(BUILDDIR)/gui_backend: $(BUILDDIR)/gui_backend.o $(BUILDDIR)/config.pb.o
$(CXX) $(LDFLAGS) -o $# $^
$(BUILDDIR)/%.o: $(SRCDIR)/%.cc $(SRCDIR)/%.h | $(BUILDDIR)
$(CXX) $(CXXFLAGS) -c -o $# $<
$(SRCDIR)/%.pb.cc: $(PROTODIR)/%.proto
$(PROTOC) $(PROTOOPTIONS) --cpp_out=$(SRCDIR) $<
now if I call to make gui_backend, I get
make: *** No rule to make target `build/config.pb.o', needed by `build/gui_backend'.
Why doesn't make pick up on the $(BUILDDIR)/%.o pattern here? If I state things explicitly by adding the rule
$(BUILDDIR)/config.pb.o: $(SRCDIR)/config.pb.cc $(SRCDIR)/config.pb.h | $(BUILDDIR)
$(CXX) $(CXXFLAGS) -c -o $# $<
it seems to work. However, I would like to keep the makefile as concise as possible.
Running make with -d gives me the following (complete output here):
Considering target file `build/config.pb.o'.
File `build/config.pb.o' does not exist.
Looking for an implicit rule for `build/config.pb.o'.
Trying pattern rule with stem `config.pb'.
Trying implicit prerequisite `src/config.pb.cc'.
Trying pattern rule with stem `config.pb'.
Trying implicit prerequisite `build/config.pb.c'.
Trying pattern rule with stem `config.pb'.
Trying implicit prerequisite `build/config.pb.cc'.
Trying pattern rule with stem `config.pb'.
[...]
Trying implicit prerequisite `build/SCCS/s.config.pb.o'.
Trying pattern rule with stem `config.pb'.
Trying implicit prerequisite `src/config.pb.cc'.
Looking for a rule with intermediate file `src/config.pb.cc'.
Avoiding implicit rule recursion.
Trying pattern rule with stem `config'.
Trying implicit prerequisite `src/proto/config.proto'.
[...]
I'm running GNU Make 3.81, btw.
Also, I just noticed that if I run make src/config.pb.cc and then make build/config.pb.o manually, it works.
Why doesn't this work?

Your example looks correct to me. The only difference between the pattern rule and the explicit rule I see is that the explicit rule doesn't have the order-only prerequisite. What version of GNU make are you running? Are you sure it supports order-only prerequisites? If it doesn't then the pattern rule will not match as it's trying to find a way to build a target like | and can't.
Whenever you hit issues like this the simplest thing to do is run make -d. The output is voluminous but it is also very enlightening: find the section where it's trying to build config.pb.o and see what patterns it's trying and why it decides to give up on that one.

I am also building proto files, I am using these Makefile targets:
%.pb.cpp: %.proto
protoc --cpp_out=. $<
mv $(#:cpp=cc) $#
%.pb.h: %.pb.cpp ;
build/%.o: %.cpp
#mkdir -p $(#D)
$(CXX) $(CXXFLAGS) $(NON_PROFILE_CXXFLAGS) -MD -MF $(#:%.o=%.d) -MT $# -c $< -o $#
I can execute
rm -rf build/lsm.pb.o lsm.pb.h lsm.pb.cpp
make build/lsm.pb.o
And it correctly finds the dependencies. For me, I was unable to get this to work until I added the semicolon to the end of the first rule. For some reason, Make does not like empty recipes for pattern rules.
Six year old question at this point, but hopefully this answer can help others like me who end up here.

Related

Makefile: Pattern rule that can ignore directories

I am trying to have a Makefile rule that can generate an object file from a source file in a directory that is specified explicitly.
exe: foo.o bar.o
foo.o: path/to/foo.c
%.o: %.c
echo Making $# from $<
This example will find it needs to make "exe", then search to make "foo.o". The "foo.o" search will try pattern rules with stem "foo" and fail to use the rule because "foo.c" doesn't exist. I want to find a way to have it see that "foo.o" can be compiled from "path/to/foo.c" using the pattern rule.
In my case it doesn't make sense for me to have the rule be "%.o: path/to/%.c" because the path should be specified for each target that needs the source to be located in another directory.
The pattern rule works for "bar.o" being made from "bar.c" within the same directory and I want to keep that working.
The solution I am going with for now is:
define c-to-o-command
echo Making $# from $<
endef
exe: foo.o bar.o
foo.o: path/to/foo.c
$(c-to-o-command)
%.o: %.c
$(c-to-o-command)
This has a drawback that the command for the pattern rule is not visible in the Makefile at the same place. It also will need to be expanded for other pattern rules that may need to have this "out of path" dependency.

How to make makefile to use pattern rule?

Having this simple makefile:
VPATH = include
CC := gcc
CFLAGS := -I include -Wall -pedantic
%: %.o include.o
$(CC) -o $# $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $# $<
When I trigger it with the name of a program (the same name as is source with .c extension), I would expect to trigger the first rule (since I provided just a name without extension) and the the second one, because for the first rule, there is %.o prerequisite, which is exactly the second rule.
Having these file in pwd:
client.c include makefile server6.c server.c
Now If I make make server:
It does
gcc -I include -Wall -pedantic server.c -o server
In other words, The second rule is not triggered. there is no step with makeing object files, even though the object file is in the first rule as prerequisite. So how is that possible? The make is simply ignoring the prerequisite and trying to make just with the first rule. How to fix that?
That's because make has a built-in rule for %: %.c and make will always choose a pattern rule that can directly create the target over a pattern rule that requires another pattern rule:
Note however, that a rule whose prerequisites actually exist or are mentioned always takes priority over a rule with prerequisites that must be made by chaining other implicit rules.
You can run make -r to remove all the built-in rules, or else remove it yourself by adding:
% : %.c
to your makefile.
You can see all built-in rules by running make -p -f/dev/null

GNU Make ignoring a phony rule specified by wildcard?

I am learning some courses about compiling some C code into specific assembly. I decided that the generated assembly should be manually inspected, so I came up with less something.s as a "test" rule.
As a fan-but-newbie of Make, I wrote this Makefile:
CODES := a
LESS ?= less
CODES_TEST := $(patsubst %,%-test,${CODES})
.PHONY: all test ${CODES_TEST} clean
all: $(patsubst %,%.s,${CODES})
test: all
%-test: %.s
${LESS} $^
%.s: %.c
${CC} ${CFLAGS} -S -o $# $^
clean:
rm -f *.o *.s
And I have this minimal a.c file:
int asdfg(void) { return 54321; }
I then typed make a-test in Bash, expecting less showing up with the content of a.s, only to be told this:
make: Nothing to be done for 'a-test'.
I got the above response regardless of the presence of a.s, which generates normally if I do make a.s or just make (implicitly runs the first rule, all).
I checked my Makefile and I don't think I made a typo or another simple mistake.
What did I miss with the above Makefile?
How can I get Make to execute less a.s when I run make a-test?
There is nothing to be done for a-test because the only rule that would make it is the implicit pattern rule:
%-test: %.s
${LESS} $^
and, per the manual 4.6 Phony Targets:
The implicit rule search (see Implicit Rules) is skipped for .PHONY targets.
and, since it is .PHONY, its mere non-existence does make it out-of-date.
To get around this, while preserving the phoiness, replace:
%-test: %.s
${LESS} $^
with:
${CODES_TEST}: %-test: %.s
${LESS} $^
Then the rule is a static pattern rule and no longer an implicit one.

make with -R looks for fileName..asm not fileName.asm

Make looking for file(dot dot).asm
I used the -R switch to avoid being overwhelmed with -d lines. I have file findKey.s. No problem using cpp extension with equivalent makefile statements.
What I assume to be the pertinent makefile lines:
CC = "f:\IAR Systems\Embedded Workbench 6.5\arm\bin\iccarm"
ASM = "f:\IAR Systems\Embedded Workbench 6.5\arm\bin\iasmarm"
all : findKey.o intVec.o invBea.o
%.o: %.cpp
$(CC) "$<" -lC "$(listDir)" -o "$(objDir)" $(dlib) $(C_allOptions) --eec++
%.o: %.c
$(CC) "$<" -lC "$(listDir)" -o "$(objDir)" $(dlib) $(C_allOptions)
%o: %.asm
$(ASM) "$<" -O"$(objDir)" -L"$(listDir)" -r -cM t8 --cpu Cortex-M4 --fpu None
%o: %.s
$(ASM) "$<" -O"$(objDir)" -L"$(listDir)" -r -cM t8 --cpu Cortex-M4 --fpu None
[BEGIN Debug output]
GNU Make 3.82
Built for x86_64-w64-mingw32
This program is built by Equation Solution <http://www.Equation.com>
for Windows.
Copyright (C) 2010 Free Software Foundation, Inc.
[snip]
Updating goal targets....
Considering target file `all'.
File `all' does not exist.
Considering target file `findKey.o'.
File `findKey.o' does not exist.
Looking for an implicit rule for `findKey.o'.
Trying pattern rule with stem `findKey'.
Trying implicit prerequisite `findKey.cpp'.
Trying pattern rule with stem `findKey'.
Trying implicit prerequisite `findKey.c'.
Trying pattern rule with stem `findKey.'.
Trying implicit prerequisite `findKey..asm'. <== WHY THIS??
Trying pattern rule with stem `findKey.'.
Trying implicit prerequisite `findKey..s'. <== AND THIS??
Trying pattern rule with stem `findKey'.
Trying implicit prerequisite `findKey.cpp'.
Looking for a rule with intermediate file `findKey.cpp'.
Avoiding implicit rule recursion.
Trying pattern rule with stem `findKey'.
Trying implicit prerequisite `findKey.c'.
Looking for a rule with intermediate file `findKey.c'.
Avoiding implicit rule recursion.
Trying pattern rule with stem `findKey.'.
Trying implicit prerequisite `findKey..asm'.
Looking for a rule with intermediate file `findKey..asm'.
Avoiding implicit rule recursion.
Trying pattern rule with stem `findKey.'.
Trying implicit prerequisite `findKey..s'.
Looking for a rule with intermediate file `findKey..s'.
Avoiding implicit rule recursion.
No implicit rule found for `findKey.o'.
Finished prerequisites of target file `findKey.o'.
Must remake target `findKey.o'.
[END Debug output]
And then it complained “no rule to make target findKey.o” and stopped.
Possible clue: I removed -R from the command line and make tried to assemble findKey.s with as which is some kind of gnu thing.
Ideas?
Look closely at your two assembler rules:
%o: %.asm
$(ASM) "$<" -O"$(objDir)" -L"$(listDir)" -r -cM t8 --cpu Cortex-M4 --fpu None
%o: %.s
$(ASM) "$<" -O"$(objDir)" -L"$(listDir)" -r -cM t8 --cpu Cortex-M4 --fpu None
Those should be %.o, not %o. That would be the source of the stray double dots in your debug output and the lack of a proper "compile .s to .o" rule.

Why does Make ignore my wildcard rule?

Why doesn't Make link to foo.o?
$ ls
foo.c foo_test.c Makefile
$ cat Makefile
.PHONY: test
test: foo_test
%_test: %_test.o foo.o
$ make
cc foo_test.c -o foo_test
Pattern rules MUST have a recipe associated with them. Any pattern rule without a recipe tells GNU make to delete that pattern rule. So, your line:
%_test: %_test.o foo.o
does nothing except delete the non-existent pattern rule to build %_test from %_test.o. You need to create a recipe if you want it to take effect:
%_test: %_test.o foo.o
$(CC) -o $# $(LIBS) $^
or whatever. However, this is completely not necessary for your example. You don't need any rule at all for that, just write:
foo_test: foo_test.o foo.o
and let make's built-in rules handle it.

Resources