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.
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. |