The warning function is very useful for debugging wayward makefile s. Because the warning function expands to the empty string, it can be placed anywhere in a makefile : at the top-level, in target or prerequisite lists, and in command scripts. This allows you to print the value of variables wherever it is most convenient to inspect them. For example:
$(warning A top-level warning) FOO := $(warning Right-hand side of a simple variable)bar BAZ = $(warning Right-hand side of a recursive variable)boo $(warning A target)target: $(warning In a prerequisite list)makefile $(BAZ) $(warning In a command script) ls $(BAZ):
yields the output:
$ make makefile:1: A top-level warning makefile:2: Right-hand side of a simple variable makefile:5: A target makefile:5: In a prerequisite list makefile:5: Right-hand side of a recursive variable makefile:8: Right-hand side of a recursive variable makefile:6: In a command script ls makefile
Notice that the evaluation of the warning function follows the normal make algorithm for immediate and deferred evaluation. Although the assignment to BAZ contains a warning , the message does not print until BAZ is evaluated in the prerequisites list.
The ability to inject a warning call anywhere makes it an essential debugging tool.
12.1.1 Command-Line Options
There are three command-line options I find most useful for debugging: ”just-print ( -n ), ”print-data-base ( -p ), and ”warn-undefined-variables .
The first test I perform on a new makefile target is to invoke make with the ”just-print ( -n ) option. This causes make to read the makefile and print every command it would normally execute to update the target but without executing them. As a convenience, GNU make will also echo commands marked with the silent modifier ( @ ).
The option is supposed to suppress all command execution. While this may be true in one sense, practically speaking, you must take care. While make will not execute command scripts, it will evaluate shell function calls that occur within an immediate context. For instance:
REQUIRED_DIRS = ... _MKDIRS := $(shell for d in $(REQUIRED_DIRS); \ do \ [[ -d $$d ]] mkdir -p $$d; \ done) $(objects) : $(sources)
As we've seen before, the purpose of the _MKDIRS simple variable is to trigger the creation of essential directories. When this is executed with ”just-print , the shell command will be executed as usual when the makefile is read. Then make will echo (without executing) each compilation command required to update the $(objects) file list.
The ”print-data-base ( -p ) option is another one you'll use often. It executes the makefile , displaying the GNU copyright followed by the commands as they are run by make , then it will dump its internal database. The data is collected into groups of values: variables, directories, implicit rules, pattern-specific variables, files (explicit rules), and the vpath search path :
# GNU Make 3.80 # Copyright (C) 2002 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. # There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. normal command execution occurs here # Make data base, printed on Thu Apr 29 20:58:13 2004 # Variables ... # Directories ... # Implicit Rules ... # Pattern-specific variable values ... # Files ... # VPATH Search Paths
Let's examine these sections in more detail.
The variables section lists each variable along with a descriptive comment:
# automatic <D = $(patsubst %/,%,$(dir $<)) # environment EMACS_DIR = C:/usr/emacs-126.96.36.199 # default CWEAVE = cweave # makefile (from `../mp3_player/makefile', line 35) CPPFLAGS = $(addprefix -I ,$(include_dirs)) # makefile (from `../ch07-separate-binaries/makefile', line 44) RM := rm -f # makefile (from `../mp3_player/makefile', line 14) define make-library libraries += sources += : $(call source-to-object,) $(AR) $(ARFLAGS) $$@ $$^ endef
Automatic variables are not printed, but convenience variables derived from them like $(<D) are. The comment indicates the type of the variable as returned by the origin function (see the Section 4.2.5 in Chapter 4). If the variable is defined in a file, the filename and line number of the definition is given. Simple and recursive variables are distinguished by the assignment operator. The value of a simple variable will be displayed as the evaluated form of the righthand side.
The next section, labeled Directories, is more useful to make developers than to make users. It lists the directories being examined by make , including SCCS and RCS subdirectories that might exist, but usually do not. For each directory, make displays implementation details, such as the device number, inode, and statistics on file pattern matches.
The Implicit Rules section follows. This contains all the built-in and user -defined pattern rules in make 's database. Again, for those rules defined in a file, a comment indicates the file and line number:
%.c %.h: %.y # commands to execute (from `../mp3_player/makefile', line 73): $(YACC.y) --defines $< $(MV) y.tab.c $*.c $(MV) y.tab.h $*.h %: %.c # commands to execute (built-in): $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@ %.o: %.c # commands to execute (built-in): $(COMPILE.c) $(OUTPUT_OPTION) $<
Examining this section is a great way to become familiar with the variety and structure of make 's built-in rules. Of course, not all built-in rules are implemented as pattern rules. If you don't find the rule you're looking for, check in the Files section where the old-style suffix rules are listed.
The next section catalogs the pattern-specific variables defined in the makefile . Recall that pattern-specific variables are variable definitions whose scope is precisely the execution time of their associated pattern rule. For example, the pattern variable YYLEXFLAG , defined as:
%.c %.h: YYLEXFLAG := -d %.c %.h: %.y $(YACC.y) --defines $< $(MV) y.tab.c $*.c $(MV) y.tab.h $*.h
would be displayed as:
# Pattern-specific variable values %.c : # makefile (from `Makefile', line 1) # YYLEXFLAG := -d # variable set hash-table stats: # Load=1/16=6%, Rehash=0, Collisions=0/1=0% %.h : # makefile (from `Makefile', line 1) # YYLEXFLAG := -d # variable set hash-table stats: # Load=1/16=6%, Rehash=0, Collisions=0/1=0% # 2 pattern-specific variable values
The Files section follows and lists all the explicit and suffix rules that relate to specific files:
# Not a target: .p.o: # Implicit rule search has not been done. # Modification time never checked. # File has not been updated. # commands to execute (built-in): $(COMPILE.p) $(OUTPUT_OPTION) $< lib/ui/libui.a: lib/ui/ui.o # Implicit rule search has not been done. # Last modified 2004-04-01 22:04:09.515625 # File has been updated. # Successfully updated. # commands to execute (from `../mp3_player/lib/ui/module.mk', line 3): ar rv $@ $^ lib/codec/codec.o: ../mp3_player/lib/codec/codec.c ../mp3_player/lib/codec/codec.c ../ mp3_player/include/codec/codec.h # Implicit rule search has been done. # Implicit/static pattern stem: `lib/codec/codec' # Last modified 2004-04-01 22:04:08.40625 # File has been updated. # Successfully updated. # commands to execute (built-in): $(COMPILE.c) $(OUTPUT_OPTION) $<
Intermediate files and suffix rules are labeled "Not a target"; the remainder are targets. Each file includes comments indicating how make has processed the rule. Files that are found through the normal vpath search have their resolved path displayed.
The last section is labeled VPATH Search Paths and lists the value of VPATH and all the vpath patterns.
For makefile s that make extensive use of user-defined functions and eval to create complex variables and rules, examining this output is often the only way to verify that macro expansion has generated the expected values.
This option causes make to display a warning whenever an undefined variable is expanded. Since undefined variables expand to the empty string, it is common for typographical errors in variable names to go undetected for long periods. The problem with this option, and why I use it only rarely, is that many built-in rules include undefined variables as hooks for user-defined values. So running make with this option will inevitably produce many warnings that are not errors and have no useful relationship to the user's makefile . For example:
$ make --warn-undefined-variables -n makefile:35: warning: undefined variable MAKECMDGOALS makefile:45: warning: undefined variable CFLAGS makefile:45: warning: undefined variable TARGET_ARCH ... makefile:35: warning: undefined variable MAKECMDGOALS make: warning: undefined variable CFLAGS make: warning: undefined variable TARGET_ARCH make: warning: undefined variable CFLAGS make: warning: undefined variable TARGET_ARCH ... make: warning: undefined variable LDFLAGS make: warning: undefined variable TARGET_ARCH make: warning: undefined variable LOADLIBES make: warning: undefined variable LDLIBS
Nevertheless, this command can be extremely valuable on occasion in catching these kinds of errors.
12.1.2 The ”debug Option
When you need to know how make analyzes your dependency graph, use the ”debug option. This provides the most detailed information available other than by running a debugger. There are five debugging options and one modifier: basic , verbose , implicit , jobs , all , and makefile , respectively.
If the debugging option is specified as ”debug , basic debugging is used. If the debugging option is given as -d , all is used. To select other combinations of options, use a comma separated list ”debug= option 1 , option 2 where the option can be one of the following words (actually, make looks only at the first letter):
Basic debugging is the least detailed. When enabled, make prints each target that is found to be out-of-date and the status of the update action. Sample output looks like:
File all does not exist. File app/player/play_mp3 does not exist. File app/player/play_mp3.o does not exist. Must remake target app/player/play_mp3.o. gcc ... ../mp3_player/app/player/play_mp3.c Successfully remade target file app/player/play_mp3.o.
This option sets the basic option and includes additional information about which files where parsed, prerequisites that did not need to be rebuilt, etc.:
File all does not exist. Considering target file app/player/play_mp3. File app/player/play_mp3 does not exist. Considering target file app/player/play_mp3.o. File app/player/play_mp3.o does not exist. Pruning file ../mp3_player/app/player/play_mp3.c. Pruning file ../mp3_player/app/player/play_mp3.c. Pruning file ../mp3_player/include/player/play_mp3.h. Finished prerequisites of target file app/player/play_mp3.o. Must remake target app/player/play_mp3.o. gcc ... ../mp3_player/app/player/play_mp3.c Successfully remade target file app/player/play_mp3.o. Pruning file app/player/play_mp3.o.
This option sets the basic option and includes additional information about implicit rule searches for each target:
File all does not exist. File app/player/play_mp3 does not exist. Looking for an implicit rule for app/player/play_mp3. Trying pattern rule with stem play_mp3. Trying implicit prerequisite app/player/play_mp3.o. Found an implicit rule for app/player/play_mp3. File app/player/play_mp3.o does not exist. Looking for an implicit rule for app/player/play_mp3.o. Trying pattern rule with stem play_mp3. Trying implicit prerequisite app/player/play_mp3.c. Found prerequisite app/player/play_mp3.c as VPATH ../mp3_player/app/player/play_mp3.c Found an implicit rule for app/player/play_mp3.o. Must remake target app/player/play_mp3.o. gcc ... ../mp3_player/app/player/play_mp3.c Successfully remade target file app/player/play_mp3.o.
This options prints the details of subprocesses invoked by make . It does not enable the basic option.
Got a SIGCHLD; 1 unreaped children. gcc ... ../mp3_player/app/player/play_mp3.c Putting child 0x10033800 (app/player/play_mp3.o) PID 576 on the chain. Live child 0x10033800 (app/player/play_mp3.o) PID 576 Got a SIGCHLD; 1 unreaped children. Reaping winning child 0x10033800 PID 576 Removing child 0x10033800 PID 576 from chain.
This enables all the previous options and is the default when using the -d option.
Normally, debugging information is not enabled until after the makefile s have been updated. This includes updating any included files, such as lists of dependencies. When you use this modifier, make will print the selected information while rebuilding makefile s and include files. This option enables the basic option and is also enabled by the all option.