There are several methods to specify build commands to update a target:
A dependency
This is more or less the same as how this is used in a Makefile: One or more targets, a colon and any number of sources. This specifies that the target(s) depends on the source(s). When build commands are given these are the commands to build the target(s) from the source(s). Without build commands the dependency is only used to check if the target is outdated and needs to be build.
A rule
Specified with a :rule command. A "%" in the target(s) and source(s) stands for any string. This is used to specify a dependency that is to be used for files that match the pattern.
An action
Specified with a :action command. Unlike dependencies and rules an action does not specify a build dependency. It must be invoked by other build commands with the :do command.
Nearly all recipe commands can be used in the build commands. But these are not allowed, they can only be used at the recipe level:
a dependency specification |
:rule |
:route |
:totype |
:clearrules |
:delrule |
:program |
:dll |
:lib |
:recipe |
:variant |
The commands :program, :lib, :dll and :ltlib are called production commands because they explicitly state what things Aap should produce and what sources are involved. Everything the production commands can do, can be done by hand with dependencies as well, but the automation the production commands provide is quite useful. This section discusses how the production commands can be used and the variables that affect them.
The form of each of the production commands is :command targets : sources. It is unusual to have more than one target, since both targets would be built from the same sources, but it is allowed. The list of sources should list the actual, original sources, i.e. only files that are actually written by the programmer and that exist on disk. It is these sources that will be packaged together for distributing the program or library in source form.
Each production command transforms all of the sources into objects using compile actions. The sources are transformed into object files of a particular type — e.g. libraries use files with type "libobject". Once all of the sources have been compiled, a build action is invoked to turn the object files into the target. The table below lists the production commands and the actions used.
Some of the production commands can use different programs to produce the final product, depending on settings in the recipe. In particular, you may need to chose to link a program with the compiler or through libtool, depending on whether your program links to any libtool libraries or not. The alternatives are listed in the table below as well. To select an alternative form to build the final product, set the filetype of the target to a specific value, e.g.
:program myProgram { filetype=ltprogram } : source.c |
Command | Object Type | Build Command | Build Alternatives |
---|---|---|---|
:program | object | build |
|
:lib | libobject | buildlib | (normal) Uses the ar utility to link all the objects into a static library. Uses $ARFLAGS. |
:dll | dllobject | builddll | (normal) Uses the C compiler to link the objects into a dynamic (shared) library. The object files are different from regular library objects, and use a different extension. Uses $SHLINK, and $LDFLAGS, as well as $SHLINKFLAGS. |
:ltlib | ltobject | buildltlib | (normal) Uses the libtool utility to link the objects together. Uses $LDFLAGS. |
In case you do want to have Aap figure out how to turn source files in to objects and then combine them into a target, but the target is not one of the types mentioned above, you can use the :produce command.
When a target is to be build Aap first searches for an explicit dependency with build commands that produces the target. This dependency may come from a high level build command such as :program. When such a dependency is not found then the rules defined with :rule are checked:
All the matching rules without commands are used, but only if the source already exists. Thus this cannot be used to depend on a file that is still to be created.
One rule with commands will be selected, in this order of preference:
A rule for which the sources exist. |
A rule for which one of the sources does not exist and was not defined with the {sourceexists} option. |
:rule test/%.html : test/%.in :do something :rule %.html : %.in :do something-else |
:rule %.jpg : path/%.jpg :copy $source $target |
When a dependency with build commands 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. Here is an 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. Aap 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. Another such variable is "source_list", it 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 |
When multiple targets are used and there are no build commands, this works as if each target depends on the list of sources. Thus this dependency:
t1 t2 : s1 s2 s3 |
t1 : s1 s2 s3 t2 : s1 s2 s3 |
When a source file includes other files, the targets that depend on the source file also depend on the included files. 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.
Aap detects these implied dependencies automatically for the types it knows about. Currently that is C and C++. Either by using gcc or a Python function the "#include" statements are found in the source code and turned into a dependency without build commands.
This works recursively. Thus when "foo.c" includes "foo.h" and "foo.h" includes "common.h", the dependency will look like this:
foo.c : foo.h common.h |
For other types of files than C and C++ you can add your own dependency checker. For example, this is how to define a checker for the "tt" filetype:
:action depend 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 Aap.
Since the build commands for ":action depend" are ordinary build commands, you can use Python commands, system commands or a mix of both to do the dependency checking.
More about customizing dependency checking in Chapter 29.
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 attributes of all the sources are used. In case the same attribute is used twice, the last one wins.
Another method is to use an "add_" attribute. This works like "var_", but instead of overruling the variable value it is appended. This is useful for variables that are a list of items, such as $DEFINE. Example:
:attr thefile.c {add_DEFINE = -DEXTRA=yes} |
Another method is to define a scope name. This scope is then used to find variables before searching other scopes, but after using the local scope. For example, to specify that the "s_opt" scope is to be used when compiling "filter.c":
OPTIMIZE = 0 DEBUG = yes :program myprog : main.c filter.c version.c :attr {scope = s_opt} filter.c s_opt.OPTIMIZE = 4 s_opt.DEBUG = no |
Note that you can set the values of the variables in the user scope after adding the scope attribute to "filter.c".
A virtual target is a target that is not an actual file. A Virtual target is used to trigger build commands without creating a file with the name of the target. Common virtual targets are "clean", "all", "publish", etc.
When a target is virtual it is always built. Aap 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 |
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 built 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 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 current directory usually is the directory in which the recipe is located, but a :cd command may change this.
The "srcpath" attribute overrules using $SRCPATH for an item. Example:
:attr bar.c {srcpath = ~/src/lib} |
To avoid using $SRCPATH for a source, so that it is only found in the current directory, 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 |
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 |
The default buildcheck is made from the build commands 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:
a filetype attribute specified after action
if the first argument doesn't contain a "$", the filetype of this argument
the filetype of the first source argument of the dependency.
To add something to the default check for the build commands the $commands variable can be used. Example:
Version = 1.4 foo.txt : {buildcheck = $commands $Version} :del {force} $target :print >$target this is $target :print >>$target version number: $Version |
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} |
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 |
Sometimes you might change the build commands in a recipe, which would normally mean the target should be updated, but you are sure that this isn't necessary and want to avoid executing the build commands. You can tell Aap to ignore the buildcheck once with the --contents option.