Debugging a makefile is a bit of black magic. Unfortunately, there is no such thing as a makefile debugger to examine the progress of a particular rule or how a variable is expanded. Most of the debugging can be done with regular prints and checking the makefile . Of course, GNU make helps a little with its built-in methods and command line options. One of the best methods for debugging makefiles is to add debug hooks and use safe programming techniques to lean on when things go wrong. The following are some basic debugging techniques and secure coding practices that I think will be most useful.
Debugging capabilities of make
A very useful function for debugging a makefile that does not work warning
. Since the function warning
expands to an empty string, it can be used anywhere in the makefile : at the top level, in the target name, in the dependency list, and in command scripts. This allows the values โโof variables to be printed where it is most appropriate for checking 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):
Gives 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
Note that the execution of the function warning
follows the normal flow of the algorithm make
for immediate and deferred computations. Also, the assignment to BAZ
contains warning
and the message is not printed until BAZ
it is expanded into the dependency list.
The ability to insert a warning
call anywhere makes it a very useful debugging tool.
Command line options
: --just-print (-n)
, --print-data-base (-p)
--warn-undefined-variables
.
--just-print
makefile โ make
--just-print (-n)
. make
makefile , . , GNU make
(@
) - .
. , . make
, shell
, . :
REQUIRED_DIRS = ...
_MKDIRS := $(shell for d in $(REQUIRED_DIRS); \
do \
[[ -d $$d ]] || mkdir -p $$d; \
done)
$(objects) : $(sources)
_MKDIRS
. --just-print
, makefile. , make
( ) $(objects)
.
--print-data-base
, . , "" makefile, make
. : , , , , ( ) vpath . .
Variables :
# automatic
<D = $(patsubst %/,%,$(dir $<))
# environment
EMACS_DIR = C:/usr/emacs-21.3.50.7
# 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 += $1
sources += $2
$1: $(call source-to-object,$2)
$(AR) $(ARFLAGS) $$@ $$^
endef
- , , , , $(<D)
. origin
(. make manual). , . . .
Directories make
, make
. make, SCCS RCS -, , . : , inode .
Implicit rules make
. , , , :
%.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) $<
make
. , . , Files, .
makefile. โ , . , :
%.c %.h: YYLEXFLAG := -d %.c %.h: %.y $(YACC.y) --defines $< $(MV) y.tab.c $*.c $(MV) y.tab.h $*.h
:
# 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
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) $<
- "Not a target"; โ . , make
. , vpath , .
VPATH Search Paths VPATH
vpath
.
makefile', eval
, - , .
--warn-undefined-variables
make
. , , . , , . make
, makefile' , :
$ 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
, .
--debug
make
, --debug
. . : basic
, verbose
, implicit
, jobs
, all
, makefile
, .
--debug
, basic
- . -d
, all
. : --debug=option1,option2
, option
( , make ):
basic
. ,make
, .
verbose
basic
, .
implicit
basic
, .
jobs
make
' .basic
.
all
-d
.
makefile
, , makefile . , .make
makefile' .basic
,all
.
,
, makefile , . , makefile , .
, , , . , , , . makefile, . , makefile . , - , , , .
ยซKISSยป โ . makefile , , . . , , .
, makefile' , , - , , C++ Java. make
! , .
makefile . , make
, makefile , :
do:
cd i-dont-exist; \
echo *.c
makefile , :
$ make
cd i-dont-exist; \
echo *.c
/bin/sh: line 1: cd: i-dont-exist: No such file or directory
*.c
, .c
, . . -, :
SHELL = /bin/bash
do:
cd i-dont-exist && \
shopt -s nullglob &&
echo *.c
cd
make
, echo
make
. , nullglob
bash
. (, .)
$ make
cd i-dont-exist && \
echo *.c
/bin/sh: line 1: cd: i-dont-exist: No such file or directory
make: *** [do] Error 1
. makefile' , , . ?
_MKDIRS := $(shell for d in $(REQUIRED_DIRS); do [[ -d $$d \
]] || mkdir -p $$d; done)
:
_MKDIRS := $(shell \
for d in $(REQUIRED_DIRS); \
do \
[[ -d $$d ]] || mkdir -p $$d; \
done)
, , , . . , - , , , .
, . , :
TAGS:
cd src \
ctags --recurse
disk_free:
echo "Checking free disk space..." \
df . | awk '{ print $$4 }'
. . , strip
. . , , .
. , . โ make . ยซ ยป .
, โ if
, , assert
, , , ( ), , , make
.
โ . make โ 3.80:
NEED_VERSION := 3.80
$(if $(filter $(NEED_VERSION),$(MAKE_VERSION)),, \
$(error You must be running make version $(NEED_VERSION).))
Java CLASSPATH
.
- .
assert
, :
# $(call assert,condition,message)
define assert
$(if $1,,$(error Assertion failed: $2))
endef
# $(call assert-file-exists,wildcard-pattern)
define assert-file-exists
$(call assert,$(wildcard $1),$1 does not exist)
endef
# $(call assert-not-null,make-variable)
define assert-not-null
$(call assert,$($1),The variable "$1" is null)
endef
assert
makefile , .
:
# $(debug-enter)
debug-enter = $(if $(debug_trace),\
$(warning Entering $0($(echo-args))))
# $(debug-leave)
debug-leave = $(if $(debug_trace),$(warning Leaving $0))
comma := ,
echo-args = $(subst ' ','$(comma) ',\
$(foreach a,1 2 3 4 5 6 7 8 9,'$($a)'))
, . debug_trace
:
$ make debug_trace=1
@
, , :
QUIET := @
โฆ
target:
$(QUIET) some command
:
$ make QUIET=