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
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'
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'
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.)
for all plugin directories just as it does for dependencies.
build-item
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: }
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>'
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>'
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.
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.