3
votes

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?

2
Just to be clear, src/config.pb.cc does not exist (at the outset), but src/config.pb.h and $(PROTODIR)/config.proto do? And when you make build/config.pb.o it builds nothing? What about when you make src/config.pb.cc? Are you sure about the PROTODIR variable? I mean, that it is what you think it is, and that the proto file is actually there? - Beta
Both, src/config.pb.cc and src/config.pb.h do not exist. They are both generated by the call to $(PROTOC). And yes, PROTODIR is set correctly. If I run make src/config.pb.cc it will call protoc correctly and generate the two files from the config.proto file. - DeX3

2 Answers

1
votes

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.

0
votes

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.