user3240688
user3240688

Reputation: 1335

Please explain this Makefile - executable rule appears to be missing

I have this makefile which I don't understand.

build_sources:=$(wildcard *.cpp)
depends:=$(build_sources:.cpp=.d)
build_targets:=$(build_sources:.cpp=)

.PHONY: all
all: $(build_targets)

.PHONY: clean
clean:
    rm -f $(build_targets) *.{a,o,d}

#build the list of header file dependencies automatically
%.d: %.cpp
    @echo building include dependencies for $(*F)
    @$(CXX) -MM $(CPPFLAGS) $< | { sed 's#\($*\)\.o[ :]*#\1.o $@ : #g' ; echo "%.h:;" ; } > $@

-include $(depends)

I understand that the executables created are build_target. So if I have Foo.cpp and Bar.cpp, the executables created would be Foo and Bar.

But how does it do that? I only see 1 rule, and it is %.d: %.cpp. So it's saying the Foo.d file depends on Foo.cpp. But what about the rule to actually compile Foo?

This makefile works, so it's not actually missing anything. But how come I don't see the rule? Is there some implicit rule?

EDIT - i did make debug, and saw the following

No need to remake target `foo.d'.
  Considering target file `foo'.
   File `foo' does not exist.
   Looking for an implicit rule for `foo'.
   Trying pattern rule with stem `foo'.
   Trying implicit prerequisite `foo.o'.
Found an implicit rule for `foo'.      ## WHAT EXACTLY IS THIS?
Considering target file `foo.o'.
 File `foo.o' does not exist.
 Looking for an implicit rule for `foo.o'.
 Trying pattern rule with stem `foo'.
 Trying implicit prerequisite `foo.c'.
 Trying pattern rule with stem `foo'.
 Trying implicit prerequisite `foo.cc'.
 Trying pattern rule with stem `foo'.
 Trying implicit prerequisite `foo.C'.
 Trying pattern rule with stem `foo'.
 Trying implicit prerequisite `foo.cpp'.
 Found an implicit rule for `foo.o'.
  Pruning file `foo.cpp'.
  Pruning file `foo.cpp'.
 Finished prerequisites of target file `foo.o'.
Must remake target `foo.o'.
g++   -I../../include -Wall -std=c++11 -O3  -Wsign-compare -Wno-strict-aliasing -s  -c -o foo.o foo.cpp

That's great. This must be the rule that's getting called. But where is this rule from? How do I know what default rules exist?

Upvotes: 4

Views: 1186

Answers (4)

Colin Pitrat
Colin Pitrat

Reputation: 2092

What you probably miss to understand this Makefile is the concept of pattern rule and automatic variables.

This part finds .cpp files:

build_sources:=$(wildcard *.cpp)

And this part creates targets with the same name (using the variable build_sources defined above) except the extension is replaced by .d:

depends:=$(build_sources:.cpp=.d)

The same kind of construction defines the build targets (same file name with extension removed):

build_targets:=$(build_sources:.cpp=)

Then the default target is defined to require build_targets, i.e the executable corresponding to the .cpp`

all: $(build_targets)

This rule defines how to build a .d from a .cpp:

#build the list of header file dependencies automatically
%.d: %.cpp
    @echo building include dependencies for $(*F)
    @$(CXX) -MM $(CPPFLAGS) $< | { sed 's#\($*\)\.o[ :]*#\1.o $@ : #g' ; echo "%.h:;" ; } > $@

gcc -MM produce rules to know on which headers a .c or .cpp file depend. Typically if test.cpp includes test1.h and test2.h the output will be:

test.o: test.cpp test1.h test2.h

The .d will contain dependencies of each cpp file and will create a ruleto build a .o file and an executable from each cpp. $@ is the target of the rule (the .d file) and will contain, unless I'm mistaken, a rule looking like this (written by the sed expression):

filename.o filename.d : filename.cpp <list of headers>
%.h:;

The first rule give dependencies of the .o files. It has no recipe which means it simply adds dependencies to any existing rule. An implicit rule will be used to build them. The second one is here in case you suppress a header. In this case, make will use this rule that says that there's simply nothing to do (it has an empty recipe).

Then all the .d files are included as part of the makefile:

-include $(depends)

Finally, the implicit rule to link a single .o file kicks in:

Linking a single object file

n is made automatically from n.o by running the linker (usually called ld) via the C compiler. The precise recipe used is ‘$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)’.

Edit: To build objects in a subdirectory obj you would have to modify each filename:

depends:=$(foreach file,$(build_sources:.cpp=.d),"obj/$(file)")

To build binaries in a separate subdirectory bin you would need to do the same for build_targets:

build_targets:=$(foreach file,$(build_sources:.cpp=), "bin/$(file)")

Then you need to write the rule to build it because the default rule wont work anymore (the .o is not in the same directory). What you want is to add a rule like this:

bin/foo: obj/foo.o
    $(CC) $(LDFLAGS) obj/foo.o $(LOADLIBES) $(LDLIBS) -o bin/foo

which can be done with the right modification of the long shell command:

    @$(CXX) -MM $(CPPFLAGS) $< | { sed 's#\($*\)\.o[ :]*#\1.o $@ : #g' ; echo "%.h:;" ; obj=`echo $< | sed 's/.cpp/.o/'` ; bin=`echo $< | sed 's/.cpp//'` ; echo "$bin: $obj" ; echo -e "\t\$(CC) \$(LDFLAGS) $obj \$(LOADLIBES) \$(LDLIBS) -o $bin" ; } > $@

Upvotes: 2

leemes
leemes

Reputation: 45705

The makefile is't complete in itself. It automatically creates the files .d which are snippets included into that makefile at the end. These files are created with the %.d-rule.

So have a look at those generated .d files to see rules for each individual object file. The rule generation script is written with sed and kind of hard to read, but in fact very simple. At first, the compiler is invoked on the .cpp file with the -MM flag, which will output something like

foo.o: foo.cpp foo.h bar.h

if foo.cpp includes its own header file foo.h as well as bar.h, for example (directly or indirectly).

The sed regex replacement command now simply adds the name of the file, in which the generated rules are about to be written (the .d file) right after the .o in the above rule, so it is also marked as depending on the sources, just like the object file. That's important when the includes in one of the sources is changed later. The sed command also adds a rule for all header files to do nothing.

foo.o foo.d: foo.cpp foo.h bar.h
%.h:;

The resulting object files .o are then linked using one of the implicit rules:

Linking a single object file

n is made automatically from n.o by running the linker (usually called ld) via the C compiler. The precise recipe used is $(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS).

[...]

Upvotes: 0

Toby Speight
Toby Speight

Reputation: 30927

Many rules are built in to Make. If you are using GNU Make, you can get it to print its built-in rules like this:

make -f /dev/null -p

You can make it ignore the built-in rules with -r; if you do this with your Makefile, you'll find that it complains that it doesn't know how to make your targets.

Upvotes: 0

Michael Kohne
Michael Kohne

Reputation: 12044

Your make package includes a large number of default rules, which this Makefile depends on. Try running your make with -d (debug info). I believe that will show you everything that is in play.

Upvotes: 0

Related Questions