Now all those $@ thingees that appear in the example above and elsewhere in the makefile are clearly not plain old macros, since they’re never defined and yet this makefile works quite well, I promise. The reason is that there are a number of special macros with one character names that are only useable as part of a dependency rule:
$@ The file name of the target.
$< The name of the first dependency.
$* The part of a filename which matched a suffix rule.
$? The names of all the dependencies newer than the target separated by spaces.
Special $ commands
One line of the sample makefile illustrates two special commands that make provides through the $ symbol: OBJ = $(addsuffix .o, $(basename $(SRC)))
This particular line sets OBJ to be the same as SRC but with all the .cc suffixes replaced with .o. The part $(basename $(SRC)) strips all the .cc’s off all the file names, and the $(addsuffix .o, …) adds the .o to what’s left. This is useful because you can easily add a new class to your project by adding its .cc filename to the definition of SRC. Its object file will automatically be added to OBJ. Having a list of all source and object files is quite useful, as you’ll soon see.
Most of the commands you use to update files are similar; to build most C++ programs, you call g++ with similar flags and really just change the name of the file you’re compiling. Make allows you to define suffix rules. In the sample, there’s one which tells it how to build a .o file from a .cc file:
.cc.o: -> $(CXX) $(CXXFLAGS) -c $<
With suffix rules, you can leave out the command part of a dependency rule, for example:
usepix.o: usepix.cc usepix.h pixmap.h
If make decides to update usepix.o, it sees from this line that it depends on usepix.cc, and it sees from the .cc.o rule that to update it, it should run g++ using the flags defined in CXXFLAGS to compile usepix.cc. In fact, the command it executes turns out to be:
$(CXX) $(CXXFLAGS)…………………………… -c $< g++ -g -I. -I/home/garrett/Projects/Tapestry/lib -c usepix.cc
In fact, make is so smart that you don’t even need to tell it what usepix.o depends on (although it is a good idea). Once it figures out that the file usepix.cc exists, it assumes that usepix.o depends upon itgif and applies the suffix rule accordingly.
Make also has a number of built in suffix rules, which are roughly as follows:
.c.o: -> $(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<
.cc.o: -> $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) -o $@ $<
.o : -> $(CC) $(LDFLAGS) $
-o $@ $(LOADLIBES)
The .SUFFIXES: .cc line tells make to apply suffix rules only to .cc files and is not absolutely necessary.
Automating dependency creation
There is a neat utility called makedepend which can go through your source code and see which files #include which other ones. It’s used like this: makedepend options source-files. For options, -Idirectory tells it to look in directory for include files, and -Ydirectory tells it not to look in directory. Just -Y by itself means not to look at any of the system include directories. You can give as many -I and -Y options as you need. Makedepend ignores any options it doesn’t understand; this is useful as you’ll see below.
If you give it -Y, it will probably spit out a bunch of stuff about not being able to find iostream.h and so on. Just ignore these messages.
After makedepend scans your source code, it tacks the resulting dependency rules onto the end of your makefile and saves you a ton of typing.
It’s very useful to have a “depend” target in your makefile which does this automatically:
depend: -> makedepend $(CXXFLAGS) -Y $(SRC)
We give makedepend the CXXFLAGS because that macro contains -I commands which are normally passed to g++ to tell it where the tapestry .h files are, but those same commands are useful here. Makedepend happily ignores any other options in CXXFLAGS such as debugging flags.