NAME¶
makepp_scanning -- How makepp finds include files and other hidden dependencies
DESCRIPTION¶
Makepp can determine additional dependencies or targets for certain commands
that it knows something about. This is especially important for C/C++
compilation, where it is too error-prone to list manually all of the include
files that a given source file depends on. By looking at the compilation
command and the source files themselves, makepp is able to determine
accurately which object files need to be rebuilt when some include file
changes.
Example: Given a rule
foo.o: # Usually %.o: %.c, just for illustration
time -p /bin/libtool -bar /usr/bin/cc -c -I somewhere foo.c
makepp knows that "time" and "libtool" must be skipped and
that "cc" is the actual command to be parsed here. It understands
that
foo.c is the input file and thus a dependency of this rule.
Moreover it will scan that file looking for include statements, also in
directory
somewhere, because it understood the command options.
Actually there are three steps to what is historically known as scanning:
- 1.
- The rule action is split into lines (continuation lines count as one).
Each line (except builtins and Perl blocks) is lexically analyzed
as one or more Shell commands. Redirections are recognized as inputs or
outputs to this rule. The first word of each command is looked up (with
its directory part but, if not found, again without it) to find a parser
for it. These become optional dependencies, they are built if possible,
but ignored if not found, as makepp can't know which part of a complex
command is actually run.
Commands in backquotes are analyzed but not executed. (Often execution is
important, but this would be a major interference by makepp.) It is better
style to avoid them. Instead have makepp run the command at most once by
assigning it in this special way:
XYZFLAGS ;= $(shell pkg-config --cflags xyz)
Currently there is only one lexer class, which understands Bourne Shell. To
better handle C Shell or "command.com", subclasses might be
created. However, much syntax is similar enough to not warrant this. Get
in touch if you want to contribute either.
- 2.
- For known commands the corresponding command parser (also referred
to just as parser) analyzes the important options and arguments. The
available ones are described below.
Even if no specialized parser was found, the generic one makes the command
executable an input of this rule. You can change that with the
--no-path-executable-dependencies command option.
- 3.
- If the parser recognized any input files, they get sent to the
scanner chosen by the parser. It finds further inputs by looking
for "#include" or comparable statements.
This is the most expensive step. All the results get cached to avoid
repeating it unnecessarily.
If makepp thinks it's compiling a C/C++ source but can't find a parser, it will
give a warning message to let you know. This usually means that you buried
your compiler command too deeply in the action for makepp to find it. For
example, I have seen rules like this:
%.o: %.c
@echo Compiling $< now; obscure_wrapper gcc -c $< $(CFLAGS) -o $@
The first words of the actions here are "echo" and
"obscure_wrapper", for which there are no parsers, so makepp will
not scan for include files in this case. You can ignore the prefixed command
by:
register-parser obscure_wrapper skip-word
The following sections document the built in parsers and scanners. In the name
you can use "-" interchangeably with "_".
SCANNERS (PARSERS)¶
The various scanners must be chosen by a command parser, which is given in
parentheses:
C/C++ compilation (c-compilation, gcc-compilation)¶
The C/C++ scanner, handles both languages indifferently. In fact it looks only
at preprocessor statements, so it can be used for quite a few languages. The
parser that activates it has a special variant for gcc's many options, which
gets chosen if the command name includes the string "gcc" or g++. If
compilers for other languages with C preprocessor use the same options as the
C compiler (at least "-I") then this parser works fine.
It looks at the command for "-Idir" options specifying the include
path or "-Ldir" options specifying the link path. It then scans any
source files for "#include" directives, and also looks at the
command line to see if there are any source files or libraries mentioned which
are not listed as dependencies. It recognizes these by their extension.
This scanner gives a warning message if files included with
"#include "file.h"" are not found, or not buildable
by makepp, in the include path, or in the directory containing the file which
is "#includ"ing, or in
/usr/include. No warning is given if a
file included with "#include <file.h>" is not found.
Makepp assumes it is in some system include directory that the compiler knows
about, and that files in system include directories won't change.
In addition, files in
/usr/include,
/usr/local/include,
/usr/X11R6/include, and any other directory which is not writable are
not scanned to see what they include. Makepp assumes that these files won't
change. (If you're running as root, the writability test is performed with the
UID and GID of the directory you ran makepp from. This is so compiling a
program as an ordinary user and then doing "makepp install"
as root won't cause extra directories to be scanned.)
This is a fairly simple-minded scanner. It will get confused if you do things
like this:
#ifdef INCLUDE_THIS
#include "this.h"
#endif
because it doesn't know about preprocessor conditionals. This is usually
harmless; it might cause additional extra files to be labeled as dependencies
(occasionally causing unnecessary rebuilds), or else it might cause makepp to
warn that the include file was not found. You can either ignore the warning
messages, or put an empty file "this.h" out there to shut makepp up.
If your compiler has a funny name, you can say either of
register-parser obscure_c_compiler c-compilation
register-parser obscure_gcc_alias gcc-compilation
Embedded SQL C/C++ compilation (esql-compilation)¶
These commands, which come with the various databases, preprocess special
sections in otherwise C/C++-like sources, and produce C/C++ headers and
sources. This finds EXEC SQL INCLUDE "filename" or $INCLUDE
"filename" directives.
These preprocessors are recognized: Altibase APRE*C/C++ (
apre),
CASEMaker DBMaker (
dmppcc), Firebird / InterBase (
gpre), IBM
DB2 (
db2 precompile, db2 prep) & Informix ESQL/C
(
esql), Ingres (
esqlc), Mimer (
esql), Oracle
(
proc), PostgreSQL (
ecpg) & YARD (
yardpc). If your
preprocessor is not recognized, you can say
register-parser obscure_esqlc_preprocessor esql-compilation
This will however only handle the style common to Informix and others: Command
arguments ending in ".ec" are files to be scanned, "-I"
defines the include path and EXEC SQL INCLUDE directives without a suffix get
".h" appended.
Swig (swig)¶
Swig (Simplified Wrapper and Interface Generator,
http://www.swig.org/) is a
program that converts a C/C++ header file into the wrapper functions needed to
make your code callable from a variety of other languages, such as Perl,
Python, Tcl, C#, Ruby, OCaml, and probably some others that I don't know
about.
Makepp understands and parses the swig command line, looking for "-I"
and "-l" options. It also knows how to scan swig's interface
definition files (
.i files) looking for %include, %import, and also
"#include" if "-includeall" is in effect.
If your swig has a funny name, you can say
register-parser obscure_swig_alias swig
Vera and Verilog (vcs_compilation)¶
If you design hardware, this will come in handy.
Ignorable wrappers (skip-word, shell)¶
Makepp recognizes the following command words and many more and skips over them
appropriately in in its search for the correct scanner:
"condor_compile", "distcc", "ignore_error",
"libtool", "noecho" "purify".
There is a variant of this which finds the nested commands in "sh -c
'command1; command2'".
If you have more such commands, you can say
register-parser command skip-word
Libtool
Libtool is a very clever compilation system that greatly simplifies making
shared libraries by hiding all the system-dependent details away in a shell
script. The only difficulty is that the library binary files are not actually
stored in the same directory as the output file--libtool actually creates a
subdirectory, ".libs", which contains the real files. This is
ordinarily not a problem, but makepp has to know where the real binaries are
if it is to link them in from a repository. At the moment, libtool libraries
(".la" files) are not linked in from repositories; they are always
rebuilt if needed. Also, makepp at the moment is not able to use the
dependency information that is stored inside the ".la" file itself.
This will hopefully change soon.
Suppressing the scan (none)¶
Sometimes you may not want a rule or a certain command to be parsed. You can
turn off parsing and thereby scanning with
register-parser cc none
Quickscan and smartscan¶
The ":quickscan" and ":smartscan" rule options, if
applicable, affect the way that files are scanned.
In ":quickscan" mode (the default), all include directives are assumed
active. This allows for very efficient scanning.
In ":smartscan" mode, an attempt is made to interpret macros and
expressions so that inactive include directives are ignored. For example, the
executable produced by compiling the following C program ought
not to
depend on
foo.h:
#if 0
#include "foo.h"
#endif
int main() { return 0; }
CUSTOM SCANNERS¶
You can specify your own parser either in a rule option like ":parser
foo", or by using the "register_parser" or
"register_command_parser" statements.
Either way, as described under "register_parser", there you must
directly or indirectly (via a class) specify a function that creates a parser
object. This object will usually create a scanner object for files, and feed
it with its findings from the command line options. These two objects will
call the parser's "add_*_dependency" methods which forward the
information to the somewhat more complicated
"Mpp::Lexer::add_*_dependency" utility functions.
However your parser function can also do this work itself for simple cases.
There are a couple of special return values if this function doesn't return a
parser object:
- "undef"
- The scan info is not cacheable and must be recalculated next time the
rule's target needs to be built.
- "p_none, p_skip_word" or "p_shell"
- These are in fact numeric constants, which tell the lexer to do the work
of these pseudo-parsers.
- any reference, e.g. "\1"
- This is equivalent to returning a parser object of the
"Mpp::CommandParser" base class, which will only additionally
make the command executable itself a dependency.
In most cases, objects of type "Mpp::CommandParser" should instantiate
at least one object of type "Mpp::Scanner". The
"Mpp::Scanner" base class takes care of the distinction between
quickscan and smartscan. Note that the behavior of "Mpp::Scanner"
can be markedly affected by this distinction, but that should be transparent
to the derived class if it is well-formed. New derived
"Mpp::Scanner" classes ought to be tested in both modes.
If you write your own "Mpp::Scanner" class, you should also base your
rescanning decision on the build info "RESCAN". This gets set by
"makeppreplay" after signing files without scanning. So despite the
signatures being consistent, a rescan is still necessary. If your
"Mpp::Scanner" uses the inherited "scan_file1" method,
you're probably fine.
For more details, refer to the respective class documentation. For examples, see
"Mpp::CommandParser::Gcc" and "Mpp::CommandParser::Vcs".
Look at the "p_" functions in
Mpp/Subs.pm which get aliased
into their respective classes as "factory" when loaded.
Caching scanner info¶
If the all of the scanner's important side effects are effected through calls to
methods of the "Mpp::CommandParser" base class, then those side
effects can be cached in the build info file, so that they can be played back
by a subsequent invocation of makepp without doing all of the costly scanning
work. This can save quite a bit of time, especially in smartscan mode.
If the scanner has other important side effects, then it should call the
"Rule" object's mark_scaninfo_uncacheable method. Otherwise, the
scanner info retrieved from the build info may be inaccurate, causing the
build result possibly to be incorrect. This method is called automatically
when a value from the %parsers hash does not return an object of type
"Mpp::CommandParser", or when the parser is specified with a rule
option and the "p_*" routine does not return an object of type
"Mpp::CommandParser".
Cached scan info is invalidated using criteria similar to those used for
determining when the target is out of date. Similarly, it can be retrieved
from a repository using criteria similar to those used for determining when a
target can be linked in from a repository.
You can force makepp to ignore the cached scanner info with the
"--force-rescan" option. This is useful when a broken scanner may
have caused incorrect scanner info to be cached.
Ad Hoc Scanner¶
Often you will have just one or few files which contain dependency information.
You don't want to write this into a makefile redundantly (since redundancy
later often leads to inconsistencies when one update gets forgotten). But you
also don't want to write a Mpp::Scanner? As a workaround you can generate an
include file on the fly. For example Qt has
.qrc files which can look
like:
<RCC>
<qresource prefix="...">
<file>abc</file>
<file>xyz</file>
...
If you adhere to the above layout, you can transform the relevant lines into a
makepp include file, which gets automatically created by being included.
%.qrc.makepp: %.qrc
&grep 's!<RCC>\n!$(stem).cc:! || s! *<file>! ! && s!</file>\n!!' $(input) -o $(output)
include $(wildcard *.qrc) # .makepp is appended automatically
Several variants of this are given in the Cookbook. The drawback is that you
begin building while reading the makefile. So the --loop command option will
not be so useful on the first iteration.