How to trace Makefile targets for troubleshooting? - makefile

We have a long and complicated Makefile in our build system. Is there a good way to trace exactly which targets get executed for a given make invocation?

Use make -d or make --debug[=flags] options:
‘-d’
Print debugging information in addition to normal processing. The debugging information says which files are being considered for remaking, which file-times are being compared and with what results, which files actually need to be remade, which implicit rules are considered and which are applied—everything interesting about how make decides what to do. The -d option is equivalent to ‘--debug=a’ (see below).
‘--debug[=options]’
Print debugging information in addition to normal processing. Various levels and types of output can be chosen. With no arguments, print the “basic” level of debugging. Possible arguments are below; only the first character is considered, and values must be comma- or space-separated.
a (all)
All types of debugging output are enabled. This is equivalent to using ‘-d’.
b (basic)
Basic debugging prints each target that was found to be out-of-date, and whether the build was successful or not.
v (verbose)
A level above ‘basic’; includes messages about which makefiles were parsed, prerequisites that did not need to be rebuilt, etc. This option also enables ‘basic’ messages.
i (implicit)
Prints messages describing the implicit rule searches for each target. This option also enables ‘basic’ messages.
j (jobs)
Prints messages giving details on the invocation of specific subcommands.
m (makefile)
By default, the above messages are not enabled while trying to remake the makefiles. This option enables messages while rebuilding makefiles, too. Note that the ‘all’ option does enable this option. This option also enables ‘basic’ messages.
Another option is to use remake - a patched version of GNU Make that adds improved error reporting, the ability to trace execution, and a debugger.

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' \
> '../../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.

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.

Related

How to trace make, which targets/prerequisites are being called? [duplicate]

We have a long and complicated Makefile in our build system. Is there a good way to trace exactly which targets get executed for a given make invocation?
Use make -d or make --debug[=flags] options:
‘-d’
Print debugging information in addition to normal processing. The debugging information says which files are being considered for remaking, which file-times are being compared and with what results, which files actually need to be remade, which implicit rules are considered and which are applied—everything interesting about how make decides what to do. The -d option is equivalent to ‘--debug=a’ (see below).
‘--debug[=options]’
Print debugging information in addition to normal processing. Various levels and types of output can be chosen. With no arguments, print the “basic” level of debugging. Possible arguments are below; only the first character is considered, and values must be comma- or space-separated.
a (all)
All types of debugging output are enabled. This is equivalent to using ‘-d’.
b (basic)
Basic debugging prints each target that was found to be out-of-date, and whether the build was successful or not.
v (verbose)
A level above ‘basic’; includes messages about which makefiles were parsed, prerequisites that did not need to be rebuilt, etc. This option also enables ‘basic’ messages.
i (implicit)
Prints messages describing the implicit rule searches for each target. This option also enables ‘basic’ messages.
j (jobs)
Prints messages giving details on the invocation of specific subcommands.
m (makefile)
By default, the above messages are not enabled while trying to remake the makefiles. This option enables messages while rebuilding makefiles, too. Note that the ‘all’ option does enable this option. This option also enables ‘basic’ messages.
Another option is to use remake - a patched version of GNU Make that adds improved error reporting, the ability to trace execution, and a debugger.
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' \
> '../../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.
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.

How do I write a custom silent rule in automake?

I'm working on an automake project and we have a custom assembler, compiler, and linker.
Currenlty building those custom targets shows the entire command line:
knucc -c -L/some/path/here -I/some/path/include -I/some/other/path -o foobar.o foobar.knucpp
How do I sprinkle in the magic to transform the above into this:
KCXX foobar.o
when I do make V=0 or turn on the silent rules by default with configure --enable-silent-rules.
I'm using automake v1.14.
I eventually found the magic on the GNU automake mailing list: http://gnu-automake.7480.n7.nabble.com/AM-V-GEN-better-docs-td3426.html
I was able to work this out:
QUIET_KCXX = $(QUIET_KCXX_$(V))
QUIET_KCXX_ = $(QUIET_KCXX_$(AM_DEFAULT_VERBOSITY))
QUIET_KCXX_0 = #printf " %-5s " KCXX ; echo $(notdir $#);
.knucxx.o:
$(QUIET_KCXX) # build command here
and now I have a custom message during silent builds.

GNU make lint prerequisite

The GNU make manual says:
A phony target should not be a prerequisite of a real target file; if it is, its recipe will be run every time make goes to update that file.
What if that's what I want?
For example, what if I have a phony target called lint that lints app/scripts/main.js, and I want it to run every time make goes to update (transpile & minify) dist/scripts/main.js?
I just removed the lint target and put its recipe as the first command of the recipe for the dist/scripts/main.js target.
dist/scripts/main.js : app/scripts/main.js
./node_modules/.bin/eslint $< && \
mkdir -p dist/scripts && \
./node_modules/.bin/babel $< | \
./node_modules/.bin/uglifyjs - --screw-ie8 -o $# -m -c
Updated answer: the current GNU make manual does not specify the cited "rule" from the question but instead explicit says to make use of this in the entry about forced targets, which includes a note:
As you can see, using FORCE this way has the same results as using .PHONY clean.
Using .PHONY is more explicit and more efficient. However, other versions of make do not support .PHONY; thus FORCE appears in many makefiles

Makefile - compiling back and forth

Following is the directory structure of my project:
expt-main
---------
Makefile_main
/ \
subdir-1 subdir-2
-------- --------
Makefile_1 Makefile_2
mod_codeA.f90 mod_code1.f90
mod_codeB.f90 mod_code2.f90
mod_codeC.f90 mod_code3.f90
Makefile_main:
export
SHELL = /bin/sh
F90 = mpxlf95
SRCDIRS = $(subdir-1) $(subdir-2)
all:
#for DIR in ${SRCDIRS} ;
do \
back=`pwd`; \
cd $$DIR ;\
$(MAKE) ; status=$$? ; \
if [ $$status != 0 ] ; then \
echo "Exit status fro make was $$status" ; exit $$status ; \
fi ; \
cd $$back ; \
done
-------------------------------------------------------------------------------
Makefile-1:
%.o: %.f90
$(F90) $(F90FLAGS) -I$(subdir-2) -c $<
mod_codeA.o: mod_codeC.o $(subdir-2)/mod_code2.o
-------------------------------------------------------------------------------
Makefile-2:
PROG = $(exec)
subdir-1_objs = $(subdir-1)/mod_codeA.o mod_codeB.o mod_codeC.o
all: $(PROG)
$(PROG): $(subdir-2_objs) $(subdir-1_objs) -o $# $(subdir-2_objs) $(subdir-1_objs)
---------------------------------------------------------------------------------
-
I've written the Makefile_main such that it compiles the codes (modules) in subdir-1 first and then the ones in subdir-2 and finally makes the executable. The issue: modules in subdir-1 uses modules from subdir-2 and in similar fashion, modules in subdir-2 uses those in subdir-1. My make is getting failed because the modules being used is in other directory. How to write a makefile which will take care of this issue that is, while compiling modules in subdir-1, whenever it encounters the need for an object file from subdir-2, it should switch to subdir-2 compile the necessary modules and return back to subdir-1 for further action?
If modules in different subdirectories need each other as you say, then this is not a good use of recursive Make.
Do away with Makefile-1 and Makefile-2, and let Makefile_main do all the work. (I can't tell you specifically how to change Makefile-main, since I don't do Fortran, I don't understand Makefile-2, and I don't see any dependency of modules in subdir-2 upon those in subdir-1).
If you want to stick to this directory layout and still keep three separated Makefiles, then you can use compiler flags to instruct the FORTRAN compiler to put module files into a common directory of your choice.
For instance using:
$ gfortran --version
GNU Fortran (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Copyright (C) 2011 Free Software Foundation, Inc.
you can use -I and -J flags to instruct the compiler on:
where to search for module files (.mod)
where to put generated module files
That said I think that the suggestion given by Beta to join the Makefiles makes a lot of sense. To know some of the reasons why you should do that you can read this paper.
Finally, as your project seems not to be very large at this stage, I also suggest to take into consideration CMake as a build system, as it possibly provides a more convenient way of specifying dependencies between targets (as well as many other things).

How do you implement a Makefile that remembers the last build target?

Let's say you have a Makefile with two pseudo-targets, 'all' and 'debug'. The 'debug' target is meant to build the same project as 'all', except with some different compile switches (like -ggdb, for example). Since the targets use different compile switches, you obviously need to rebuild the entire project if you switch between the two. But GNUmake doesn't naturally recognize this.
So if you type make all you'll get
Building ...
...
Then if you type make debug, you get
make: Nothing to be done for `debug'.
So my question is: how do you implement a clean solution in the Makefile to notice that the last build used a different pseudo-target, or different compile switches, than the one you want currently? If they are different, the Makefile would rebuild everything.
Put the build products into different directory trees (whilst keeping one copy of the source of course). That way you are always just a short compile from an up-to-date build, be it debug or release (or even others). No possibility of confusion either.
EDIT
Sketch of the above.
src := 1.c 2.c 3.c
bare-objs := ${src:%.c=%.o}
release-objs := ${bare-objs:%=Release/%}
debug-objs := ${bare-objs:%=Debug/%}
Release/prog: ${release-objs}
Debug/prog: ${debug-objs}
${release-objs}: Release/%.o: %.c # You gotta lurve static pattern rules
gcc -c $< -o $#
${debug-objs}: Debug/%.o: %.c
gcc -c $< -o $#
Release/prog Debug/prog:
gcc $^ -o $#
.PHONY: all
all: Release/prog ; echo $# Success
.PHONY: debug
debug: Debug/prog ; echo $# Success
(Disclaimer: not tested, nor even run through make.)
There you go. It's even -j safe so you can do make -j5 all debug. There is a lot of obvious boiler plate just crying out for tidying up.
Keeping variant sets of object files (as in bobbogo's solution) is probably the best way, but if for some reason you don't want to do that, you can use empty files as markers, to indicate which way you last built the executable:
%-marker:
#rm -f $(OBJECTS) *-marker
#touch $#
debug: GCCFLAGS += -ggdb
debug: SOMEOTHERFLAG = WHATEVER
all debug: % : %-marker
#echo making $#
#$(MAKE) -S GCCFLAGS='$(GCCFLAGS)' SOMEOTHERFLAG='$(SOMEOTHERFLAG)' main
There are other variants on this idea; you could have a small file containing the flag settings, which the makefile would build and include. That would be clever, but not really any cleaner than this.
The only clean solution is to incorporate the difference into the target names.
E.g. you can define a variable $(DEBUG) and consistently use it in all targets that depend on the compile step.

Resources