Check makefiles for missing prerequisites (dependencies).
In any sizable project, the makefiles are almost always wrong in subtle ways.
Problems can remain undiagnosed indefinitely. People tend to sense the
problems exist and develop a mistrust of the build system resorting to
frequently doing "make clean" wasting time. ("My program was behaving oddly,
so I did a make clean;make
and now it's working right.")
This tool uses GNU make and strace to find missing prerequisites in makefiles.
As a simple example:
foo:
cp bar foo
The rule uses bar when building foo but doesn't list bar as a prerequisite. The correct makefile would be:
foo: bar
cp bar foo
-
GNU make 3.82. auditmake uses the
.ONESHELL
feature introduced in GNU make version 3.82.Although 3.82 was released several years ago (July 2010), it introduced a few backward incompatibilities and so you might not have it installed. http://lists.gnu.org/archive/html/info-gnu/2010-07/msg00023.html
Run the command
make -v
to determine what version of make is installed.To install 3.82:
wget http://ftp.gnu.org/gnu/make/make-3.82.tar.bz2 tar xjf make-3.82.tar.bz2 cd make-3.82 ./configure --prefix=/usr/local/make-3.82 # or wherever you prefer make sudo make install
-
strace
-
perl
Put the following at the top of your makefile:
SRCROOT=...
ifdef AUDITMAKE
ifeq (,$(filter shortest-stem,$(.FEATURES)))
$(error You need GNU make version 3.82 or later, you have $(MAKE_VERSION))
endif
.ONESHELL:
SHELL=auditmake
.SHELLFLAGS=$(SRCROOT) $@ $^
endif
Set the SRCROOT
line to be the root of your source code tree. auditmake
ignores files that are outside this tree.
The auditmake script should be in your PATH
. Or else change the SHELL=
line to be an absolute path name.
Many build systems generate *.d
files listing the header file dependencies
of C/C++ source files. Normally, these *.d
files are removed by a make clean
, but this script needs them. So, you should do a full build, save the
*.d
files, make clean
, restore the *.d
files, then run this script. For
example:
make
find -name \*.d | tar -czf saved.tar.gz --files-from -
make clean
tar -xzf saved.tar.gz
export MAKE=/usr/local/make-3.82/bin/make
$MAKE AUDITMAKE=1
If auditmake finds any missing prerequisites, you'll see output like the following:
auditmake: foo.c used the following without specifying as prerequisite:
bar.h
baz.h
Via the SHELL
and .SHELLFLAGS
variables, make
passes to auditmake
information about what target is to be built, what direct prerequisites were
listed, and what commands should be executed to build the target. auditmake
executes the commands via strace and then analyzes strace's output.
auditmake watches for the following syscalls:
-
open(). Excludes O_DIRECTORY, O_TRUNC and O_WRONLY because these are not inputs.
-
openat(AT_FDCWD,...). Same as open(2).
-
getcwd(), clone(), chdir(). auditmake uses this information to convert relative path names to absolute.
There are probably more syscalls that auditmake should watch (e.g., mmap()).
strace version 4.5.20 on 64-bit hosts can't trace 32-bit executables. You see the following message:
[ Process PID=7569 runs in 32 bit mode. ]
strace doesn't fail, it just doesn't generate the trace.
auditmake uses a heuristic to avoid analyzing .PHONY targets: after running commands to build a target, if the target does not exist, auditmake assumes the target was .PHONY. In this case auditmake does not analyze the strace output. If the target was .PHONY but the target actually exists anyhow, the heuristic fails. auditmake will mistakenly analyze the strace output and tend to report some false missing prerequisites.