26.4. Plugin Examples

In this section, we present examples of using abuild's plugin facility. The examples here illustrate all of the capabilities of abuild's plugin system, albeit with simplistic cases. Plugins are a very powerful feature that can be used to do things that you could not otherwise do with abuild. If you are not careful, they can also create situations that violate some of abuild's design principles, so plugins should be used with particular care. You should also be careful not to overuse plugins. Many things you may consider implementing as a plugin would be better implemented as an ordinary build item with rules or hooks. Plugins should be used only for adding capabilities that can't be added without plugins or that should apply broadly and transparently across many items in the build tree.

Abuild enforces that no plugin may have dependencies or be declared as a dependency of another build item. Still, it's good practice to name plugins by placing them in a private namespace. This prevents build trees that may have access to these items (but may not presently declare them as plugins) from declaring them as dependencies. In these examples, we always place our plugins in the plugin namespace by starting their names with plugin. even though we have no actual plugin build item. In order to use the plugins in this tree, we have to declare them as plugins in the root build item's Abuild.conf:

plugin/Abuild.conf

child-dirs: plugins java other
plugins: plugin.echoer plugin.printer plugin.counter

26.4.1. Plugins with Rules and Interfaces

Here we examine the plugin.counter plugin, which can be found in doc/example/plugin/plugins/counter. This is a trivial plugin that illustrates use of an interface file and also creates a custom rule that can be referenced in the RULES variable of a build item's Abuild.mk file. There's nothing special about the plugin's Abuild.conf file:

plugin/plugins/counter/Abuild.conf

this: plugin.counter
parent-dir: ..

The plugin.interface file declares a new interface variable called TO_COUNT which contains a list of file names:

plugin/plugins/counter/plugin.interface

declare TO_COUNT list filename append

This file gets loaded automatically before any regular build items' Abuild.interface files. The file count.mk in the rules/platform-independent directory is the file that a build item may include by placing RULES := count in its Abuild.mk file:

plugin/plugins/counter/rules/platform-independent/count.mk

all:: count

# Make sure the user has asked for things to count.
ifeq ($(words $(TO_COUNT)), 0)
$(error plugin.counter: TO_COUNT is empty)
endif

# Use echo `wc` to normalize whitespace
count:
        for i in $(TO_COUNT); do echo `wc -l $$i`; done

If a build item includes count in the value of its RULES variable, then any files listed in TO_COUNT will have their lines counted with wc -l when the user runs abuild with the count target. The intention here is that items that the target build item depends on would add files to TO_COUNT in their Abuild.interface files. Then the build item that actually uses the count rule would display the line counts of all of the files named by its dependencies.

This is admittedly a contrived example, but it illustrates an important point. Here we are adding some functionality that enables a build item to make use of certain information provided by its dependencies through their Abuild.interface files. Although we could certainly add the count target using a normal build item that users would depend on and access through BUILD_ITEM_RULES, doing it that way would be somewhat more difficult because each item that wanted to add to TO_COUNT would also have to depend on something that declares the TO_COUNT interface variable. By using a plugin, we cause the plugin's plugin.interface to be automatically loaded by all build items in the build tree. That way, any build item can add to TO_COUNT without having to take any other special actions. This type of facility could be particularly useful for adding support to abuild for other programming languages that require other information to be known from its dependencies.

Note that, although we have added only a rule under rules/platform-independent, we could have also added rules under rules/object-code, including even adding a rule with the same name that does something different when used by an object-code build item. This is another capability that would not be convenient from a build item's Rules.mk file.

For an example of a build item that uses this plugin's capabilities, see the build items under doc/example/plugin/other/indep. Here we have the build item indep-a in the a directory that adds a file to TO_COUNT in its Abuild.interface:

plugin/other/indep/a/Abuild.conf

this: indep-a
platform-types: indep
parent-dir: ..

plugin/other/indep/a/Abuild.interface

TO_COUNT = a-file

We also have the build item indep-b (which depends on indep-a) in the b directory that uses the count rule in its RULES variable in its Abuild.mk file:

plugin/other/indep/b/Abuild.conf

this: indep-b
platform-types: indep
parent-dir: ..
deps: indep-a

plugin/other/indep/b/Abuild.mk

RULES := count

Here is the output of running abuild count from the plugin/other/b directory:

example.count-b.out

abuild: indep-b (abuild-indep): count
make: Entering directory `--topdir--/plugin/other/indep/b/abuild-indep'
10 ../../a/a-file
make: Leaving directory `--topdir--/plugin/other/indep/b/abuild-indep'

26.4.2. Adding Backend Code

Here we examine the plugin.echoer plugin in the plugins/echoer directory. This plugin adds hook code for both ant-based and make-based build items, again something that could not be done by simply using build item rules. This very simple plugin causes a message to be printed when running the all target for make-based build items and as a post-compile hook for ant-based build items. All build items in any build tree that includes this plugin in its list of plugins will get this functionality automatically without having to take any explicit action. This would be preferable to declaring this as a dependency for every item and modifying BUILD_ITEM_RULES or abuild.hook-build-items for every build item:

plugin/plugins/echoer/plugin.mk

all:: echo ;

echo::
        @$(PRINT) This is message from the echoer plugin.

plugin/plugins/echoer/plugin-ant.xml

<project name="plugin.echoer-hooks">

 <target name="-post-compile">
  <echo message="This is a post-compile hook from the echoer plugin."/>
 </target>

</project>

Although this example is trivial and doesn't do anything useful, there are other cases in which this type of facility might be useful. Examples could include targets that gather statistics or run static analysis checks that may be required by a certain project.

For an example of using this plugin, build any build item in the other or java directories of example/plugin. Here is the output of running abuild all in the build item from the previous example. Since the count target is declared as a dependency of all in count.mk, we see the output of the count target as well as the output from the plugin.echoer build item:

example.echo-b.out

abuild: indep-b (abuild-indep): all
make: Entering directory `--topdir--/plugin/other/indep/b/abuild-indep'
This is message from the echoer plugin.
10 ../../a/a-file
make: Leaving directory `--topdir--/plugin/other/indep/b/abuild-indep'

26.4.3. Platforms and Platform Type Plugins

In the plugin.printer plugin defined in the plugins/printer directory, we create a new platform type and corresponding platform. This is the mechanism that would be used to add support to abuild for an embedded platform, a cross compiler, or some other special environment. In this example, we stretch the idea of platform types a bit for the purpose of illustrating this capability with a simple example.

Here we define a new platform type called printer. This is done by creating a platform-types file and declaring the platform type in it:

plugin/plugins/printer/platform-types

platform-type printer

In addition to adding the platform type, we also add a platform called zzprinter.any.test-suite.abc. [44] To add this platform, we print its name from the list_platforms command:

plugin/plugins/printer/list_platforms

#!/usr/bin/env perl

require 5.008;
BEGIN { $^W = 1; }
use strict;

print "platform zzprinter.any.test-suite.abc -type printer\n"

In this case, the program is trivial, but in a real implementation, the list_platforms command would probably be checking the environment or path for presence of certain tools before emitting the name of the platform. A list_platforms program should only mention the name of a platform that can actually be built on the build host from which it is run.

The fourth field of any object-code platform is always the name of the compiler, so this implies that we have an abc compiler defined somewhere. This plugin also provides the rules for using the abc compiler in toolchains/abc.mk:

plugin/plugins/printer/toolchains/abc.mk

.LIBPATTERNS = shlib-% lib-%
OBJ := obj
LOBJ := obj
define libname
lib-$(1)
endef
define binname
print-$(1)
endef
define shlibname
shlib-$(1)$(if $(2),.$(2)$(if $(3),.$(3)$(if $(4),.$(4))))
endef

ABC := $(abDIR_plugin.printer)/bin/abc
ABCLINK := $(abDIR_plugin.printer)/bin/abc-link

DFLAGS :=
OFLAGS :=
WFLAGS :=

PREPROCESS_c := @:
PREPROCESS_cxx := @:
COMPILE_c := $(ABC)
COMPILE_cxx := $(ABC)
LINK_c := $(ABCLINK)
LINK_cxx := $(ABCLINK)
CCXX_GEN_DEPS := @:

# Usage: $(call include_flags,include-dirs)
define include_flags
        $(foreach I,$(1),-I$(I))
endef

# Usage: $(call make_obj,compiler,pic,flags,src,obj)
define make_obj
        $(1) $(3) -c $(4) -o $(5)
endef

# Usage: $(call make_lib,objects,library-filename)
define make_lib
        cat $(1) > $(call libname,$(2))
endef

# Usage: $(call make_bin,linker,compiler-flags,linker-flags,objects,libdirs,libs,binary-filename)
define make_bin
        $(1) $(2) $(3) $(foreach I,$(4),-o $(I)) \
                   $(foreach I,$(5),-L $(I)) \
                   $(foreach I,$(6),-l $(I)) \
                   -b $(call binname,$(7))
endef

# Usage: $(call make_shlib,linker,compiler-flags,linker-flags,objects,libdirs,libs,shlib-filename,major,minor,revision)
define make_shlib
        $(1) $(2) $(3) $(foreach I,$(4),-o $(I)) \
                   $(foreach I,$(5),-L $(I)) \
                   $(foreach I,$(6),-l $(I)) \
                   -b $(call shlibname,$(7),$(8),$(9),$(10))
endef

To understand this file, you should read through the comments in make/rules/object-code/ccxx.mk in the abuild distribution (Appendix D, The ccxx.mk File). In this case, our plugin also creates the compiler itself in bin/abc and bin/abc-link. Our “compilers” here just create text files of the source code with numbered lines. Doing this particular operation with a plugin is a bit absurd—using some external utility would be a better implementation. Still, it illustrates the mechanics of setting up an additional platform type, and it is not at all uncommon for a native compiler plugin to provide wrappers around the real compiler.

Note that to invoke our compiler, the abc.mk file uses $(abDIR_plugin.printer) to refer to a file in its own directory, just as would be necessary in a Rules.mk file. Abuild provides these variables and corresponding ant properties (abuild.dir.build-item) for all plugin directories just as it does for dependencies.

To see this plugin in action, build the build item in other/bin with --with-deps. You will see not only the normal native executable program being built, but you will also see a second output directory called abuild-zzprinter.any.test-suite.abc which contains a file called print-program. This happens because both the bin build item and the lib build item on which it depends include the printer platform type in their platform-types keys in their Abuild.conf files:

plugin/other/lib/Abuild.conf

this: lib
parent-dir: ..
platform-types: native printer

plugin/other/bin/Abuild.conf

this: bin
parent-dir: ..
platform-types: native printer
deps: lib

Here is the build output:

example.plugin-other-bin.out

abuild: lib (abuild-<native>): all
make: Entering directory `--topdir--/plugin/other/lib/abuild-<native>'
This is message from the echoer plugin.
Compiling ../lib.cc as C++
Creating lib library
make: Leaving directory `--topdir--/plugin/other/lib/abuild-<native>'
abuild: bin (abuild-<native>): all
make: Entering directory `--topdir--/plugin/other/bin/abuild-<native>'
This is message from the echoer plugin.
Compiling ../main.cc as C++
Creating program executable
make: Leaving directory `--topdir--/plugin/other/bin/abuild-<native>'
abuild: lib (abuild-zzprinter.any.test-suite.abc): all
make: Entering directory `--topdir--/plugin/other/lib/abuild-zzprinter.a\
\ny.test-suite.abc'
This is message from the echoer plugin.
Compiling ../lib.cc as C++
Creating lib library
make: Leaving directory `--topdir--/plugin/other/lib/abuild-zzprinter.an\
\y.test-suite.abc'
abuild: bin (abuild-zzprinter.any.test-suite.abc): all
make: Entering directory `--topdir--/plugin/other/bin/abuild-zzprinter.a\
\ny.test-suite.abc'
This is message from the echoer plugin.
Compiling ../main.cc as C++
Creating program executable
make: Leaving directory `--topdir--/plugin/other/bin/abuild-zzprinter.an\
\y.test-suite.abc'

Here is the print-program file. This file contains the concatenation of all the source files used to create the executable as well as the “libraries” it “links” against:

example.printer-program.out

------ ==> ../../lib/abuild-zzprinter.any.test-suite.abc/lib-lib <== ------


------ ../lib.cc ------

1: #include "lib.hh"
2: 
3: #include <iostream>
4: 
5: void f()
6: {
7:     std::cout << "I am a function named f." << std::endl;
8: }

------ main.obj ------


------ ../main.cc ------

1: #include <iostream>
2: #include "lib.hh"
3: 
4: int main()
5: {
6:     f();
7:     std::cout << "I, this program, am aware of myself." << std::endl;
8:     std::cout << "Does that mean I'm alive?" << std::endl;
9:     return 0;
10: }

26.4.4. Plugins and Externals

In the example/plugin/outside build tree, we have a tree that includes our plugin tree as an external by listing “..” in the external-dirs key of its Abuild.conf file. This tree contains the prog2 build item which depends on the same lib as our previous example's bin build item. This build tree does not declare any plugins, so even though its external declares plugins, those plugins are not used within this tree. When we build the prog2 build item with dependencies, although the lib build item still builds as before, prog2 completely disregards the existence of the other platform type and the echoer's additional build steps. This is very important. Sometimes, a build tree may declare a plugin that works for every item in its own tree but that would not necessarily work for external items. Examples might include strict static analyzers or other code checkers. It may be desirable to allow the products of this build tree to be usable by others that do not wish to follow the same restrictions. Here is the output of building prog2 with dependencies:

example.plugin-outside.out

abuild: lib (abuild-<native>): all
make: Entering directory `--topdir--/plugin/other/lib/abuild-<native>'
This is message from the echoer plugin.
make: Leaving directory `--topdir--/plugin/other/lib/abuild-<native>'
abuild: lib (abuild-zzprinter.any.test-suite.abc): all
make: Entering directory `--topdir--/plugin/other/lib/abuild-zzprinter.a\
\ny.test-suite.abc'
This is message from the echoer plugin.
make: Leaving directory `--topdir--/plugin/other/lib/abuild-zzprinter.an\
\y.test-suite.abc'
abuild: prog2 (abuild-<native>): all
make: Entering directory `--topdir--/plugin/outside/prog2/abuild-<native>'
Compiling ../main.cc as C++
Creating prog2 executable
make: Leaving directory `--topdir--/plugin/outside/prog2/abuild-<native>'

26.4.5. Native Compiler Plugins

In the example/native-compiler directory, we have a plugin that defines a native compiler. The plugin is in the compiler directory and is called plugin.compiler. In this plugin, we are adding a new platform to support our alternative compiler. We don't have to add any new platform types since we are just adding this platform to the native platform type. Since this is a relatively common operation, abuild provides a short syntax for doing it. Here is the list_platforms program:

native-compiler/compiler/list_platforms

#!/usr/bin/env perl
BEGIN { $^W = 1; }
use strict;

my $lowpri = '';
if ((exists $ENV{'QCC_LOWPRI'}) && ($ENV{'QCC_LOWPRI'} eq '1'))
{
    $lowpri = ' -lowpri';
}
if (! ((exists $ENV{'NO_QCC'}) && ($ENV{'NO_QCC'} eq '1')))
{
    print "native-compiler$lowpri qcc.release\n";
    print "native-compiler$lowpri qcc.debug\n";
    print "native-compiler$lowpri qcc\n";
}

It generates this output which automatically creates platforms with the same first three fields (os, cpu, and toolset) as other native platforms, with the qcc compiler as the fourth field, and with release, debug, or nothing as the fifth field:

native-compiler qcc.release
native-compiler qcc.debug
native-compiler qcc

Since new platforms take precedence over old platforms by default when abuild chooses which platform to use for a given platform type, our list_platforms script offers the user a way of suppressing this platform and also of making these low priority compilers. In this case, our list_platforms program doesn't generate any output if the NO_QCC environment variable is set, and if the QCC_LOWPRI environment variable is set, it declares these as low priority compilers which makes them available but prevents them from being selected by default over built-in compilers or compilers declared by earlier plugins. Setting that environment variable would make that platform completely unavailable, regardless of any compiler preferences expressed by the user. (We could also prevent the platform using this compiler from being built by default without making it disappear entirely by using platform selectors as discussed in Section 21.1, “Platform Selection”). Note that we generate output for the qcc compiler with the release and debug flags as per our usual convention. By placing the compiler with no options last, we make abuild select it by default over the other two. It will also be selected over any built-in platforms or platforms provided by earlier plugins.

In addition to listing the compiler in list_platforms, we have to provide a support file for it in toolchains/qcc.mk:

native-compiler/compiler/toolchains/qcc.mk

.LIBPATTERNS = lib-%
OBJ = o
LOBJ = o
define libname
lib-$(1)
endef
define binname
bin-$(1)
endef
define shlibname
shlib-$(1)$(if $(2),.$(2)$(if $(3),.$(3)$(if $(4),.$(4))))
endef

QCC = echo

DFLAGS =
OFLAGS =
WFLAGS =

# Convention: clear OFLAGS with debug option and DFLAGS with release option.
ifeq ($(ABUILD_PLATFORM_OPTION), debug)
OFLAGS =
endif
ifeq ($(ABUILD_PLATFORM_OPTION), release)
DFLAGS =
endif

PREPROCESS_c = @:
PREPROCESS_cxx = @:
COMPILE_c = $(QCC)
COMPILE_cxx = $(QCC)
LINK_c = $(QCC)
LINK_cxx = $(QCC)
CCXX_GEN_DEPS = @:

# Usage: $(call include_flags,include-dirs)
define include_flags
        $(foreach I,$(1),-I$(I))
endef

# Usage: $(call make_obj,compiler,pic,flags,src,obj)
define make_obj
        $(1) make-obj $(5)
        touch $(5)
endef

# Usage: $(call make_lib,objects,library-filename)
define make_lib
        $(QCC) make-lib $(call libname,$(2))
        touch $(call libname,$(2))
endef

# Usage: $(call make_bin,linker,compiler-flags,linker-flags,objects,libdirs,libs,binary-filename)
define make_bin
        $(1) make-bin $(call binname,$(7))
        touch $(call binname,$(7))
endef

# Usage: $(call make_shlib,linker,compiler-flags,linker-flags,objects,libdirs,libs,shlib-filename,major,minor,revision)
define make_shlib
        $(1) make-bin $(call shlibname,$(7),$(8),$(9),$(10))
        touch $(call shlibname,$(7),$(8),$(9),$(10))
endef

This file illustrates a degenerate compiler implementation, providing minimal implementations of all the variables and functions that ccxx.mk requires. For details, please read the comments in make/toolchains/ccxx.mk in the abuild distribution (Appendix D, The ccxx.mk File).

In the native-compiler/outside directory, there is another build tree that lists “..” in its external-dirs key:

native-compiler/outside/Abuild.conf

this: outside
external-dirs: ..
platform-types: native
deps: lib

This tree doesn't know about the qcc compiler, so when we build the outside build item, it would build only with the default native compiler. In a default invocation of abuild (i.e., one without any platform selectors), the lib build item on which this depends would only be built with qcc because of the plugin in its build tree (which is an external relative to this tree). However, the lib build item could also be built with the default native compiler. Abuild recognizes this fact and will therefore compile lib with both qcc and the default native compiler. This is an example of abuild's ability to add additional build platforms as needed based on the dependency graph:

example.as-needed-platforms.out

abuild: lib (abuild-<native>): all
make: Entering directory `--topdir--/native-compiler/lib/abuild-<native>'
Compiling ../lib.cc as C++
Creating lib library
make: Leaving directory `--topdir--/native-compiler/lib/abuild-<native>'
abuild: lib (abuild-<native-os-data>.qcc): all
make: Entering directory `--topdir--/native-compiler/lib/abuild-<native-\
\os-data>.qcc'
Compiling ../lib.cc as C++
make-obj lib.o
Creating lib library
make-lib lib-lib
make: Leaving directory `--topdir--/native-compiler/lib/abuild-<native-o\
\s-data>.qcc'
abuild: outside (abuild-<native>): all
make: Entering directory `--topdir--/native-compiler/outside/abuild-<nat\
\ive>'
Compiling ../outside.cc as C++
Creating outside executable
make: Leaving directory `--topdir--/native-compiler/outside/abuild-<native>'

26.4.6. Checking Project-Specific Rules

Another use of a plugin could be to enforce additional build tree-specific rules that fall outside of abuild's normal dependency checking capabilities. As an example, suppose you had a build item that you wanted all build items to depend on and that you couldn't make it a plugin because it had to build something. [45] You could have that build item set a variable to some specific value in its Abuild.interface file. Then you could create a plugin that would check that the variable had that value, which would effectively make sure everyone depended on the item that set the variable. This plugin would have a plugin.mk file that would check to make sure that the variable was set and report an error if not. Since all build items would see the plugin code, it would make this plugin an effective checker for enforcing some rule that can't otherwise by expressed.

We illustrate this pattern in our rule-checker example which can be found in doc/example/rule-checker. This directory includes four build items: plugin.auto-checker, auto-provider, item1, and item2. The goal is that every build item whose target type is object-code should depend on auto-provider. This rule is enforced with the plugin.auto-checker plugin which is declared as a plugin in the tree's root Abuild.conf:

rule-checker/Abuild.conf

child-dirs: auto-checker auto-provider item1 item2
plugins: plugin.auto-checker

The plugin.auto-checker build item contains two files aside from its Abuild.conf. It has a plugin.interface file that declares a variable that indicates whether the auto-provider build item has been seen:

rule-checker/auto-checker/plugin.interface

declare SAW_AUTO_PROVIDER boolean
fallback SAW_AUTO_PROVIDER = 0

This plugin interface file is automatically loaded by all build items before their own interface files or any of the interface files of their dependencies. We include a fallback assignment of a false value to this variable. The auto-provider build item sets this variable to true in its Abuild.interface file:

rule-checker/auto-provider/Abuild.interface

# The plugin.auto-checker plugin must be enabled on any build tree
# whose item depend on this since its plugin.interface file provides a
# declaration for the SAW_AUTO_PROVIDER variable.  Additionally, the
# plugin.auto-checker plugin makes sure everyone depends on this
# item.  This item cannot itself be a plugin because it has an
# Abuild.mk file.

SAW_AUTO_PROVIDER = 1

# Make the automatically generated file visible
INCLUDES = $(ABUILD_OUTPUT_DIR)

For completeness, here are the rest of the files from auto-provider:

rule-checker/auto-provider/Abuild.mk

LOCAL_RULES := provide-auto.mk

rule-checker/auto-provider/provide-auto.mk

all:: auto.h

auto.h:
        @$(PRINT) Generating $@
        echo '#define AUTO_VALUE 818' > $@

Since auto-provider sets the SAW_AUTO_PROVIDER variable, it possible for the plugin.auto-checker build item to detect that auto-provider is in the dependency list by checking the value of that variable. It does this in its plugin.mk file, which is included by abuild's make code for every make-based build item:

rule-checker/auto-checker/plugin.mk

ifeq ($(ABUILD_TARGET_TYPE), object-code)
 ifeq ($(SAW_AUTO_PROVIDER), 0)
 $(error This item is supposed to depend on auto-provider, but it does not)
 endif
endif

To see what happens when a build item forgets to depend on auto-provider, we will look at item1. Here is its Abuild.conf:

rule-checker/item1/Abuild.conf

this: item1
parent-dir: ..
platform-types: native

As you can see, there is no dependency on auto-provider. When we try to build this item, we get the following error:

example.rule-checker-item1-error.out

abuild: item1 (abuild-<native>): all
make: Entering directory `--topdir--/rule-checker/item1/abuild-<native>'
../../auto-checker/plugin.mk:3: *** This item is supposed to depend on a\
\uto-provider, but it does not.  Stop.
make: Leaving directory `--topdir--/rule-checker/item1/abuild-<native>'

This is the error that was issued from plugin.auto-checker's plugin.mk above. The build item item2 does declare the appropriate dependency:

rule-checker/item2/Abuild.conf

this: item2
parent-dir: ..
platform-types: native
deps: auto-provider

Its build proceeds normally:

example.rule-checker-item2-build.out

abuild: auto-provider (abuild-indep): all
make: Entering directory `--topdir--/rule-checker/auto-provider/abuild-i\
\ndep'
Generating auto.h
make: Leaving directory `--topdir--/rule-checker/auto-provider/abuild-indep'
abuild: item2 (abuild-<native>): all
make: Entering directory `--topdir--/rule-checker/item2/abuild-<native>'
Compiling ../item2.c as C
Creating item2 executable
make: Leaving directory `--topdir--/rule-checker/item2/abuild-<native>'

This examples shows how little code is required to implement your own rule checking. The possibilities for use of this technique are endless. Such techniques could be used to enforce all sorts of project-specific architectural constraints, build item naming conventions, or any number of other possibilities.

26.4.7. Install Target

Still another use of plugins could be to implement an install target. Although abuild provides most of what is required to use build products within the source tree, in most real systems, there comes a time when a distribution has to be created. You can write your own install target or similar using plugins. [46]



[44] This odd name has been picked to facilitate testing of all examples in abuild's own automated test suite. By starting the platform name with zz, we effectively ensure that it will always appear alphabetically after whatever the real native platform is on our build system.

[45] Recall that plugins are not allowed to build anything themselves. This may prove to be too great of a restriction and may be relaxed somewhat in a future version of abuild.

[46] At present, there is no way to add new targets except for in a single build item using abuild's ant support. This limitation will likely be addressed in a future version of abuild.