Chapter 12. Dependencies, Rules and Actions

Automatic dependency checking

When a source file includes other files, the targets that depend on it need to be rebuild. Thus when "foo.c" includes "foo.h" and "foo.h" is changed, the build commands to produce "foo.o" from "foo.c" must be executed, even though "foo.c" itself didn't change.

A-A-P detects these implied dependencies automatically for the types it knows about. Currently that is C and C++. For other types of files you can add your own dependency checker. For example, this is how to define a checker for the "tt" file type:

   foo : foo.tt
	:sys tt_compiler $source > $target
   :autodepend tt
   	:sys tt_checker $source > $target

The "tt_checker" command reads the file "$source" and writes a dependency line in the file "$target". This is a dependency like it is used in a recipe. In a makefile this has the same syntax, thus tools that produce dependencies for "make" will work. Here is an example:

   foo.o : foo.tt  foo.hh  include/common.hh

This is interpreted as a dependency on "foo.hh" and "include/common.hh". Note that "foo.o" and "foo.tt" are ignored. Tools designed for "make" produce these but they are irrelevant for A-A-P.

Since the build commands for ":autodepend" are ordinary build commands, you can use Python commands, system commands or a mix of both to do the dependency checking.

Multiple targets

When a dependency has more than one target, this means that the build commands will produce all these targets. This makes it possible to specify build commands that produce several files at the same time. Example that compiles a file and at the same time produces a documentation file:

   foo.o foo.html : foo.src
	:sys srcit $source -o $(target[0]) --html $(target[1])

People used to "make" must be careful, they might expect the build commands to be executed once for each target. A-A-P doesn't work that way, because the above example would be impossible. To run commands on each target this must be explicitly specified. Example:

   dir1 dir2 dir3 :
   	@for item in target_list:
	     :mkdir $item

The variable "target_list" is a Python list of the target items. "source_list" is the list of source files (this excludes virtual items; "depend_list" also has the virtual items). An extreme example of executing build commands for each combination of sources and targets:

   $OUTPUTFILES : $INPUTFILES
   	@for trg in target_list:
	    :print start of file  >! $trg
	    @for src in source_list:
		:sys foofilter -D$trg $src >> $trg

RECIPE STRUCTURE

Generally there are these sections in a recipe used for building an
application:

1. global settings, include recipes with (project, user) settings
2. automatic configuration
3. specify variants
4. generic build rules
5. explicit dependencies

For larger projects sections can be moved to other recipes.

1. global settings, include recipes with (project, user) settings
	When the recipe is part of a project, it's often useful to move
	settings (and rules) that apply to the whole project to one file.
	User preferences (e.g. configuration choices) should be in a separate
	file that the user edits (using a template)
2. automatic configuration
	Find out properties of the system, and handle user preferences.  This
	may result in building the application in a different way.
3. specify variants
	Usually debug and release, but can include many more choices (type of
	GUI, small or big builds, etc.)
4. generic dependencies and build commands
	Rules that define dependencies and build commands that apply to
	several files.
5. explicit dependencies and build commands
	Dependencies and build commands that apply to specific files.


RECIPE EXECUTING:

This is done in these steps:
1. Read the startup recipes, these define default rules and variables.  These
   recipes are used:
   - "default.aap" from the distribution
   - all recipes matching "/usr/local/share/aap/startup/*.aap"
   - all recipes matching "~/.aap/startup/*.aap"
2. Read the recipe and check for obvious errors.
3. Execute the toplevel items in the recipe.  Dependencies and rules are
   stored.
4. Apply the clever stuff to add missing dependencies and rules.
5. Update the targets.  The first of the following that exists is used:
	- targets specified on the command line
	- the items in $TARGET
	- the "all" target
6. If the "finally" target is specified, execute its build commands.

When there is one item in $TARGET, no dependency for $TARGET is specified and
$SOURCE is not empty, a dependency is automatically generated. This will have
the form:

	$TARGET : $SOURCE_OBJ
		:sys $CC $CFLAGS $LDFLAGS -o $target $source

Where $SOURCE_OBJ is $SOURCE with all items changed to use $OBJSUF, if there
is a suffix.  Example:

	TARGET = foo
	SOURCE = main.c extra.p

Results in:

	foo : main$OBJSUF extra$OBJSUF
		:sys $CC $CFLAGS $LDFLAGS -o $target $source

This then uses the default rules to make "main$OBJSUF" from main.c and
extra$OBJSUF from extra.p.


DEPENDENCIES:

A dependency can have several targets.  How this is interpreted depends on
whether commands are defined.

Without commands the dependency is used like each target depends on the list
of source files.  Thus this dependency:

	t1 t2 : s1 s2 s3

Can be rewritten as:

	t1 : s1 s2 s3
	t2 : s1 s2 s3

Thus when t1 is outdated to s1, s2 or s3, this has no consequence for t2.

When commands are specified, the commands are expected to produce all these
targets. Example:

	t1 t2 : s1 s2 s3
		:sys produce s1 s2 s3  > t1
		:sys filter < t1  > t2

When either t1 or t2 is outdated relative to s1, s2 or s3, the commands are
executed and both t1 and t2 are updated.

Only when no dependency is specified for a target, the rules defined with
":rule" are checked.  All the matching rules without commands are used.
TRICK: For the rules with commands, only the matching one with the longest
pattern is used.  If there are two with an equally long pattern, this is an
error.
TRICK: When the source and target in a rule are equal, it is skipped.  This
avoids that a rule like this becomes cyclic:
	:rule %.jpg : path/%.jpg
		:copy $source $target


ATTRIBUTES OVERRULING VARIABLES

Most variables like $CFLAGS and $BDIR are used for all source files.
Sometimes it is useful to use a different value for a group of files.  This is
done with an attribute that starts with "var_".  What follows is the name of
the variable to be overruled.  Thus attribute "var_XYZ" overrules variable
"XYZ".

The overruling is done for:
- dependencies
- rules
- actions
- the default dependency added for $SOURCE and $TARGET

The attributes of all the sources are used.  In case the same attribute is
used twice, the last one wins.


VIRTUAL TARGETS

When a target is virtual it is always build.  A-A-P does not remember if it was
already done a previous time.  However, it is only build once for an invocation of Aap.  Example:
	clean:
		:del {r}{f} temp/*

To remember the signatures for a virtual target use the "remember" attribute:

	version {virtual}{remember} : version.txt.in
		:print $VERSION | :cat - $source >! version.txt

Now "aap version" will only execute the ":print" command if version.txt.in has
changed since the last time this was done.

Using {remember} for one of the known virtual targets (e.g., "all" or
"fetch") is unusual, except for "publish".

When using {remember} for a virtual target without a dependency, it will only
be build once.  This can be used to remember the date of the first invocation.

	all: firsttime
	firsttime {virtual}{remember}:
		:print First build on $DATESTR > firstbuild.txt

The difference with a direct dependency on "firstbuild.txt" is that when this
file is deleted, it won't be build again.


SOURCE PATH

The sources for a dependency are searched for in the directories specified
with $SRCPATH.  The default is ". $BDIR", which means that the sources are
searched for in the current directory and in the build directory.

The "srcpath" attribute overrules using $SRCPATH for an item.
To avoid using $SRCPATH for a source, make the "srcpath: attribute empty:

	foo.o : src/foo.c {srcpath=}

When setting $SRCPATH to include the value of other variables, you may want to
use "$=", so that the value of the variable is not expanded right away but
when $SRCPATH is used.  This is especially important when appending to
$SRCPATH before a ":variant' command, since it changes $BDIR.  Example:

	SRCPATH $+= include

Warning: Using the search path means that the first encountered file will be
used.  When old files are lying around the wrong file may be picked up.  Use
the full path to avoid this.


When a target depends on the existence of a directory, it can be specified
this way:

	foodir/foo : foodir {directory}
		:print >$target this is foo

The directory will be created if it doesn't exist.  The normal mode will be
used (0777 with umask applied).  When a different mode is required specify it
with an octal value: {directory = 0700}.  The number must start with a zero.


CROSS REFERENCER

A separate module in the A-A-P project is the cross referencer.  It can be
used to lookup where symbols are defined and used.  The database that the
cross referencer uses can be generated with a recipe.  That makes it possible
to put the right files in the database.

The standard way to create or update the database is:

	aap reference

The recipe can specify a "reference" target to implement it.  Example:

	SOURCE = main.c version.c
	INCLUDE = common.h
	
	...

	reference:
		:sys aref -u $SOURCE $INCLUDE

The advantage over letting the cross referencer use all files is that backup
files and old versions are skipped, and files in other locations can be
included.

When there is no "reference" target, the default is to execute this command:

	:do reference $SOURCE $?INCLUDE

The default for the "reference" action is to run "aref -u", thus this does
almost the same as the example.


BUILD COMMAND SIGNATURE

A special kind of signature is used to check if the build commands have
changed.  An example:

	foo.o : {buildcheck = $CFLAGS} foo.c
		:sys $CC $CFLAGS -c $source -o $target

This defines a check for the value of $CFLAGS.  When the value changes, the
build commands will be executed.

The default buildcheck is made from the command themselves.  This is with
variables expanded before the commands have been executed.  Thus when one of
the commands is ":sys $CC $CFLAGS $source" and $CC or $CFLAGS changes, the
buildcheck signature changes.  The ":do" commands are also expanded into the
commands for the action specified.  However, this only works when the action
and filetype can be estimated.  The action must be specified plain, not with a
variable, and the filetype used is the first of:
1. a filetype attribute specified after action
2. if the first argument doesn't contain a "$", the file type of this argument
3. the filetype of the first source argument of the dependency.

To combine the check for the build commands themselves with specific variable
values the $commands variable can be used:

	VERSION = 1.4
	foo.txt : {buildcheck = $commands $VERSION}
		:del {force} $target
		:print  >$target this is $target
		:print >>$target version number: $VERSION

If you now change the value of $VERSION, change one of the ":print" commands
or add one, "foo.txt" will be rebuild.

To simplify this, $xcommands can be used to check the build commands after
expanding variables, thus you don't need to specify $VERSION:

	foo.txt : {buildcheck = $xcommands}

However, this only works when all $VAR in the commands can be expanded and
variables used in python commands are not expanded.

To avoid checking the build commands, use an empty buildcheck.  This is useful
when you only want the target to exist and don't care about the command used
to create it:

	objects : {buildcheck = }
		:print "empty" > objects

If you want to ignore the buildcheck once, use the "--contents" option when
running Aap.