Running Linux, 5th Edition - Matthias Kalle Dalheimer [389]
papaya$ make CFLAGS="-DDEBUG -DBSD" ...
GNUmake offers something called pattern rules, which are even better than suffix rules. A pattern rule uses a percent sign to mean "any string." So C source files would be compiled using a rule such as the following:
%.o: %.c
gcc -c -o $@ $(CFLAGS) $<
Here the output file %.o comes first, and the dependency %.c comes after a colon. In short, a pattern rule is just like a regular dependency line, but it contains percent signs instead of exact filenames.
We see the $< string to refer to the dependency, but we also see $@, which refers to the output file. So the name of the .o file is plugged in there. Both of these are built-in macros; make defines them every time it executes an entry.
Another common built-in macro is $*, which refers to the name of the dependency stripped of the suffix. So if the dependency is edit.c, the string $*.s would evaluate to edit.s (an assembly-language source file).
Here's something useful you can do with a pattern rule that you can't do with a suffix rule: add the string _dbg to the name of the output file so that later you can tell that you compiled it with debugging information:
%_dbg.o: %.c
gcc -c -g -o $@ $(CFLAGS) $<
DEBUG_OBJECTS = main_dbg.o edit_dbg.o
edimh_dbg: $(DEBUG_OBJECTS)
gcc -o $@ $(DEBUG_OBJECTS)
Now you can build all your objects in two different ways: one with debugging information and one without. They'll have different filenames, so you can keep them in one directory:
papaya$ make edimh_dbg
gcc -c -g -o main_dbg.o main.c
gcc -c -g -o edit_dbg.o edit.c
gcc -o edimh_dbg main_dbg.o edit_dbg.o
Multiple Commands
Any shell commands can be executed in a makefile. But things can get kind of complicated because make executes each command in a separate shell. So this would not work:
target:
cd obj
HOST_DIR=/home/e
mv *.o $HOST_DIR
Neither the cd command nor the definition of the variable HOST_DIR has any effect on subsequent commands. You have to string everything together into one command. The shell uses a semicolon as a separator between commands, so you can combine them all on one line:
target:
cd obj ; HOST_DIR=/home/e ; mv *.o $$HOST_DIR
One more change: to define and use a shell variable within the command, you have to double the dollar sign. This lets make know that you mean it to be a shell variable, not a macro.
You may find the file easier to read if you break the semicolon-separated commands onto multiple lines, using backslashes so that make considers them to be on one line:
target:
cd obj ; \
HOST_DIR=/home/e ; \
mv *.o $$HOST_DIR
Sometimes makefiles contain their own make commands; this is called recursive make. It looks like this:
linuxsubdirs: dummy
set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
The macro $(MAKE) invokes make. There are a few reasons for nesting makes. One reason, which applies to this example, is to perform builds in multiple directories (each of these other directories has to contain its own makefile). Another reason is to define macros on the command line, so you can do builds with a variety of macro definitions.
GNUmake offers another powerful interface to the shell as an extension. You can issue a shell command and assign its output to a macro. A couple of examples can be found in the Linux kernel makefile, but we'll just show a simple example here:
HOST_NAME = $(shell uname
-n)
This assigns the name of your network node—the output of the uname -n command—to the macro HOST_NAME.
make offers a couple of conventions you may occasionally want to use. One is to put