如何跟踪Makefile目标以进行故障排除?

时间:2012-06-07 16:03:57

标签: makefile gnu-make

我们的构建系统中有一个冗长而复杂的Makefile。是否有一种很好的方法可以准确地跟踪为给定的make调用执行哪些目标?

3 个答案:

答案 0 :(得分:26)

使用make -dmake --debug[=flags] options

  

‘-d’

     

除正常处理外,还打印调试信息。 调试信息说明正在考虑重建哪些文件,正在比较哪些文件时间以及结果是什么,哪些文件实际需要重新制作,考虑哪些隐式规则以及哪些是应用的 - 一切有趣的是如何决定做什么。 -d选项相当于‘--debug=a’(见下文)。

     

‘--debug[=options]’

     

除正常处理外,还打印调试信息。可以选择各种级别和类型的输出。没有参数,打印“基本”级别的调试。可能的论点如下;只考虑第一个字符,值必须以逗号或空格分隔。

     

a(全部)   启用所有类型的调试输出。这相当于使用'-d'。

     

b(基本)   基本调试打印发现过期的每个目标,以及构建是否成功。

     

v(详细)   高于‘basic’的级别;包括有关解析哪些makefile的消息,不需要重建的先决条件等。此选项还会启用‘basic’消息。

     

i(隐含)   打印描述每个目标的隐式规则搜索的消息。此选项还会启用‘basic’条消息。

     

j(工作)   打印消息,提供有关特定子命令调用的详细信息。

     

m(makefile)   默认情况下,尝试重新生成makefile时未启用上述消息。此选项也可在重建makefile时启用消息。请注意,‘all’选项确实启用了此选项。此选项还会启用‘basic’条消息。

另一种选择是使用remake - GNU Make的修补版本,它增加了改进的错误报告,跟踪执行的能力以及调试器。

答案 1 :(得分:2)

ElectricMake可以生成构建日志的XML标记版本,其中包含大量有助于此情况的信息:

  • 构建期间调用的所有命令的完整命令行(即使是那些使用@修饰符标记为“静默”命令的命令行。)
  • 调用的命令的来源(makefile和行号)。
  • 命令的运行时。
  • 构建中目标之间的依赖关系。
  • 构建中目标与递归之间的结构关系。
  • 由构建中调用的命令读取/写入的文件。

以下是该输出的示例:

<job id="J0824ab08" thread="5e72bb0" node="linbuild1-2" type="rule" name="../../i686_Linux/testmain/testmain.d" file="../config/rules.mak" line="109">
<command line="110">
<argv>echo Rebuilding '../../i686_Linux/testmain/testmain.d'</argv>
<output src="prog">Rebuilding ../../i686_Linux/testmain/testmain.d
</output>
</command>
<command line="111-114">
<argv>set -e; g++ -MM -w  -DUSE_PROFILING -DUSE_LOGGING -DHAVE_UNIX -DHAVE_LINUX -I.. testmain.cpp \
        | sed 's!\(testmain\)\.o[ :]*!../../i686_Linux/testmain/\1.o '../../i686_Linux/testmain/testmain.d' : !g' \
        &gt; '../../i686_Linux/testmain/testmain.d'; \
        [ -s '../../i686_Linux/testmain/testmain.d' ] || touch '../../i686_Linux/testmain/testmain.d'</argv>
</command>
<opList>
<op type="read" file="/home/ericm/src/testmain/testmain.cpp"/>
<op type="read" file="/home/ericm/src/minidumper/ExceptionReport.h"/>
<op type="read" file="/home/ericm/src/util/ECAssert.h"/>
<op type="create" file="/home/ericm/i686_Linux/ecloud/testmain/testmain.d" found="0"/>
</opList>
<timing invoked="1.919926" completed="3.600491" node="linbuild1-2"/>
<waitingJobs idList="J0824ae38"/>
</job>

How to Quickly Navigate an Unfamiliar Makefile显示了使用带注释的构建日志来查找makefile的方法的示例。

Data Mining ElectricAccelerator Annotation显示了如何使用带注释的构建日志为构建生成物料清单。

ElectricMake与GNU Make兼容,因此它可以处理与GNU make一起使用的makefile。

免责声明:我是ElectricAccelerator的架构师和首席开发人员。

答案 2 :(得分:2)

通过在GNU make中重新定义SHELL变量,您可以获得有关正在构建哪些目标以及为什么建立这些目标的一些信息:

__ORIGINAL_SHELL:=$(SHELL)
SHELL=$(warning Building $@$(if $<, (from $<))$(if $?, ($? newer)))$(TIME) $(__ORIGINAL_SHELL)

例如,在trace-targets.mk中:

__ORIGINAL_SHELL:=$(SHELL)
SHELL=$(warning Building $@$(if $<, (from $<))$(if $?, ($? newer)))$(TIME) $(__ORIGINAL_SHELL)

all: aa.stamp ba.stamp

%.stamp:
    echo stamp > $(@)

stamp-clean:
    rm -vf *.stamp

clean: stamp-clean

.PHONY: %.phony
%.phony:
    echo $(@)

aa.stamp: ab.stamp
ab.stamp: ac.stamp

ba.stamp: bb.stamp
bb.stamp: bc.phony

清理后运行trace-targets.mk

$ make -f trace-targets.mk
trace-targets.mk:9: Building ac.stamp
echo stamp > ac.stamp
trace-targets.mk:9: Building ab.stamp (from ac.stamp) (ac.stamp newer)
echo stamp > ab.stamp
trace-targets.mk:9: Building aa.stamp (from ab.stamp) (ab.stamp newer)
echo stamp > aa.stamp
trace-targets.mk:18: Building bc.phony
echo bc.phony
bc.phony
trace-targets.mk:9: Building bb.stamp (from bc.phony) (bc.phony newer)
echo stamp > bb.stamp
trace-targets.mk:9: Building ba.stamp (from bb.stamp) (bb.stamp newer)
echo stamp > ba.stamp

然后在没有清洁的情况下再次运行trace-targets.mk

$ make -f trace-targets.mk
trace-targets.mk:18: Building bc.phony
echo bc.phony
bc.phony
trace-targets.mk:9: Building bb.stamp (from bc.phony) (bc.phony newer)
echo stamp > bb.stamp
trace-targets.mk:9: Building ba.stamp (from bb.stamp) (bb.stamp newer)
echo stamp > ba.stamp

实际上,我在makefile中执行的操作是添加以下代码段:

ifneq ($(filter all targets,$(VERBOSE)),)
__ORIGINAL_SHELL:=$(SHELL)
SHELL=$(warning Building $@$(if $<, (from $<))$(if $?, ($? newer)))$(TIME) $(__ORIGINAL_SHELL)
endif

然后我按照以下方式运行我的makefile来查看跟踪:

make VERBOSE=all
# or
make VERBOSE=targets

全部/目标的原因是因为我还有其他冗长的内容,例如:

ifneq ($(filter all vars,$(VERBOSE)),)
dump_var=$(info var $(1)=$($(1)))
dump_vars=$(foreach var,$(1),$(call dump_var,$(var)))
else
dump_var=
dump_vars=
endif

# used like

$(call dump_vars,SHELL VERBOSE)

有时我想选择性地启用/禁用调试方面。

如果您有许多makefile,可以将其放在一个通用文件中并从其他文件中包含进来。


John Graham-Cumming 的信誉,他在Tracing rule execution in GNU Make中描述了此方法。