26

我们的构建系统中有一个又长又复杂的 Makefile。有没有一种好方法可以准确跟踪给定 make 调用执行了哪些目标?

4

3 回答 3

31
于 2012-06-07T16:30:33.417 回答
3

ElectricMake can generate an XML-marked-up version of your build log with lots of information that would help in this situation:

  • The full command-lines for all commands invoked during the build (even those that were marked as "silent" commands with the @ modifier).
  • The origin (makefile and line number) of the commands invoked.
  • Runtime of the commands.
  • Dependency relationships between the targets in the build.
  • Structural relationship between targets and recursive makes in the build.
  • Files read/written by the commands invoked in the build.

Here's a sample of that output:

<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 shows an example of using the annotated build log to find your way around a makefile.

Data Mining ElectricAccelerator Annotation shows how you can use the annotated build log to generate a bill-of-materials for the build.

ElectricMake is GNU Make compatible, so it can process makefiles that work with GNU make.

Disclaimer: I'm the architect and lead developer of ElectricAccelerator.

于 2012-06-07T17:12:26.753 回答
3

You can get some information about what targets are being built and why by redefining the SHELL variable in GNU make:

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

For example, in 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

Running trace-targets.mk after clean:

$ 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

Then running trace-targets.mk again without cleaning:

$ 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

Practically, what I do in my makefiles is I add this snippet:

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

Then I run my makefiles as follow to see the tracing:

make VERBOSE=all
# or
make VERBOSE=targets

The reason for the all/targets is because I also have other verbose stuff like this:

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)

And sometimes I want to selectively enable/disable debugging aspects.

If you have many makefiles it may make sense to put this in a common file and include it from others.


Credit by John Graham-Cumming who described this method in Tracing rule execution in GNU Make.

于 2020-01-14T13:49:59.970 回答