Depurar um makefile é um pouco de magia negra. Infelizmente, não existe um depurador makefile para examinar o progresso de uma regra específica ou como uma variável é expandida. A maior parte da depuração pode ser feita com impressões regulares e verificação do makefile . Claro, GNU make ajuda um pouco com seus métodos embutidos e opções de linha de comando. Uma das melhores técnicas para depurar makefiles é adicionar ganchos de depuração e usar técnicas de programação seguras para se apoiar quando as coisas dão errado. A seguir estão algumas técnicas básicas de depuração e práticas seguras de codificação que considero mais úteis.
Recursos de depuração do make
Uma função muito útil para depurar um makefile que não funciona warning
. Uma vez que a função se warning
expande para uma string vazia, ela pode ser usada em qualquer lugar no makefile : no nível superior, no nome do destino, na lista de dependências e em scripts de comando. Isso permite que os valores das variáveis sejam impressos onde for mais apropriado para validá-los. Por exemplo:
$(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):
Dá saída:
$ 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
Observe que a execução da função warning
segue o fluxo normal do algoritmo make
para cálculos imediatos e adiados. Além disso, a atribuição a BAZ
contém warning
e a mensagem não é impressa até BAZ
que seja expandida para a lista de dependências.
A capacidade de inserir uma warning
chamada em qualquer lugar o torna uma ferramenta de depuração muito útil.
Opções de linha de comando
: --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=