Chapter 37. Attributes

Attributes can be added to an item with the :attr command and by using them in a dependency or rule. Note that an assignment does not directly associate the attribute with a node. This only happens when the variable is used in an :attr command or a dependency.

The form for an attribute is:

        {name = value} 

"value" is expanded like other items, with the addition that "}" cannot appear outside of quotes.

This form is also possible and uses the default value of 1:

        {name} 

Examples:

        bar : thatfile {check = $MYCHECK}
        foo {virtual} : somefile

The "virtual" attribute is used for targets that don't exist (as file or directory) but are used for selecting the dependency to be built. These targets have the "virtual" attribute set by default:

Table 37.1. Virtual Targets

TargetCommonly used for
allbuild the default targets
cleanremove generated files that are not distributed (added automatically)
cleanmoreremove all generated files (added automatically)
cleanALLremove all generated files, AAPDIR and build-* directories below the toplevel recipe
  
testrun tests
checksame as "test"
installbuild and install for use (added automatically)
uninstalluninstall for use (added automatically)
tryoutbuild and install for trying out
  
reference generate or update the cross-reference database
  
fetchobtain the latest version of each file
updatefetch and build the default targets
  
checkoutcheckout (and lock) from version control system
commitcommit changes to VCS without unlocking
checkincheckin and unlock to VCS
unlockunlock files from a VCS
addadd new files to VCS
removeremove deleted files from VCS
reviselike checkin + remove
tagadd a tag to the current version
  
prepareprepare for publishing (generated docs but no exe)
publishdistribute all files for the current version
  
finallyalways executed last (using "aap finally" is uncommon)


The targets marked with "(added automatically)" will be added by Aap if they are not present. This is done for the toplevel and each child recipe.

These specific targets may have multiple build commands. They are all executed to update the virtual target. Normally there is up to one target in each (child) recipe.

Note that virtual targets are not related to a specific directory. Make sure no other item in this recipe or any child recipe has the same name as the virtual target to avoid confusion. Specifically using a directory "test" while there also is a virtual target "test". Name the directory "testdir" to avoid confusion.

The "comment" attribute can be used for targets that are to be specified at the command line. "aap comment" will show them.

        % aap comment
        target "all": build everything
        target "foo": link the program 

Sticky Attributes

When attributes are used in a rule or dependency, most of them are only used for that dependency. But some attributes are "sticky": Once used for an item they are used everywhere for that item. Sticky attributes are:

Table 37.2. Sticky attributes

virtualvirtual target, not a file
remembervirtual target that is remembered
directoryitem is a directory
filetypetype of file
constantfile contents never changes
fetchlist of locations where to fetch from (first one that works is used)
commitlist of locations for VCS
publishlist of locations to publish to (they are all used)
forcerebuild a target always
depdirdirectory to put an automatically generated dependency file in; when omitted $BDIR is used
var_BDIRdirectory to put the related object or generated file in; when omitted $BDIR is used
signfilefile used to store signatures for this target


The check attribute

The check attribute is used to specify what kind of signature is used for an item.

The default check for a file that was changed is an md5 checksum. Each time a recipe is executed the checksums for the relevant items are computed and stored in the file "AAPDIR/sign". The next time the recipe is executed the current and the old checksums are compared. When they are different, the build commands are executed. This means that when you put back an old version of a file, rebuilding will take place even though the timestamp of the source might be older than the target.

Another check can be specified with {check = name}, where "name" is the kind of check. Example:

        foo.txt : foo.db {check = time}
                :sys db_extract $source >$target

The default check is "md5". This is specified with the $DEFAULTCHECK variable. You can set this variable to "time" or "newer" to use timestamps instead of md5 signatures. The value of $DEFAULTCHECK is used when a node does not have a "check" attribute.

Table 37.3. supported check attribute values

timeBuild the target when the timestamp of the source differs from the last time the target was built.
newerBuild the target if its timestamp is older than the timestamp of the source. This is what the good old "make" program uses.
md5Build the target if the md5 checksum of the source differs from the last time the target was built. This is the default.
c_md5Like "md5", but ignore changes in comments and amount of white space. Appropriate for C programs. Slows down computations considerably.
noneDon't check time or contents, only existence. Used for directories.


When mixing "newer" with other methods, the build rules are executed if the target is older than the source with the "newer" check, or when one of the signatures for the other items differs.

The "AAPDIR/sign" file is normally stored in the directory of the target. This means it will be found even when using several recipes that produce the same target. But for targets that get installed in system directories (use an absolute path), virtual targets and remote targets this is avoided. For these targets the "AAPDIR/sign" file is stored in the directory of the recipe that specifies how to build the target.

To overrule the directory where the "sign" file is written, use the attribute {signdirectory = name} for the target. To overrule the file where the signatures are written, use the attribute {signfile = name} for the target. "name" cannot end in "sign".

Handling Circular Dependencies

Two attributes can be used to handle circular dependencies:

updateCan be set to "no" to avoid updating a source that a target depends on.
recursiveCan be set to a number, which indicates the maximum recursive depth allowed.

The use can best be illustrated with an example:

      :attr {recursive = 3} index file.out

      index: file.out {update = no}
              # Get the current checksum for the index file.
              @sum = get_md5("index")

              # Generate the new index file from the output file.
              :system wc file.out >$target

              # Update the output file if the index file changed.
              @if sum != get_md5("index"):
                :update file.out

      file.out: file.in index {update = no}
              # Make sure index exists.
              @if not os.path.exists("index"):
                :print empty > index

              # Generate the output file.
              :cat $source >! $target

              # Need to generate the index file again.
              :update index

      all: file.out

The goal is to produce the file "file.out". It is created from "test.in" and "index". The "index" is created from "file.out", which includes the "index" file, thus a circular dependency exists. The idea is to repeat generating "file.out" until it no longer changes.

The "recursive" attribute is set to 3 for "index" and "file.out". This allows rebuilding "file.out" three times before giving up.

In the first dependency the "{update = no}" attribute is used to avoid updating "file.out". The build commands first update the "index" file before using :update to update "file.out". But this is only done when the index file has changed. That is where the circular dependency stops: When the generated index file no longer changes.

In the second dependency a similar thing is done: The "index" file is not updated before executing the build commands but as part of the build commands.