Reputation: 1335
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
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
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
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
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