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
tree-name: plugin child-dirs: plugins java other outside echo 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
name: plugin.counter
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/all directory is the
file that a build item may include by placing RULES :=
count in its Abuild.mk file:
plugin/plugins/counter/rules/all/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, 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.
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
name: indep-a platform-types: indep
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
name: indep-b platform-types: indep 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:
count-b.out
abuild: build starting 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' abuild: build complete
Here we examine the plugin.echoer plugin
in the plugins/echoer directory. This
plugin supplies automatic build code for both Groovy-based and
make-based build items, something that cannot be done using
ordinary build item-supplied rules. This very simple plugin
causes a message to be printed when running the
all target. The contents of the message have
a default value but can be influenced by changes to a variable
that users can make in their individual build files. 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
RULES or abuild.rules for
every build item.
Here we show the code for both the make and Groovy backends in
plugin.mk and
plugin.groovy respectively:
plugin/plugins/echoer/plugin.mk
all:: echo ;
echo::
@$(PRINT) This is a message from the echoer plugin.
@$(PRINT) The value of ECHO_MESSAGE is $(ECHO_MESSAGE)
plugin/plugins/echoer/plugin.groovy
abuild.addTargetClosure('echo') {
ant.echo("This is a message from the echoer plugin.")
ant.echo("The value of echo.message is " + abuild.resolve('echo.message'))
}
abuild.addTargetDependencies('all', 'echo')
Observe that the make version refers to the variable
ECHO_MESSAGE and the Groovy version refers to
the parameter echo.message. Where do these
come from? The answer is that default values are provided by
pre-plugin initialization files in
preplugin.mk and
preplugin.groovy.
[58]
The pre-plugin initialization code is loaded
before your build file
(Abuild.mk or
Abuild.groovy), while the
plugin.mk and
plugin.groovy files are loaded after your
build file. Here are the files:
plugin/plugins/echoer/preplugin.mk
ECHO_MESSAGE = default message
plugin/plugins/echoer/preplugin.groovy
parameters {
echo.message = 'default message'
}
Although this example is trivial and doesn't do anything useful, it does illustrate how you can use pre-plugin initialization along with regular plugin code to interact with the user's build files. Although adding specific code without going through the usual rules method should generally be used sparingly, 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, or code that enforces policy.
Although building any item in the plugin
tree will illustrate use of the
plugin.echoer plugin, the
echo directory contains four items
specifically designed to illustrate manipulation of the echo
message. Under the plugin/echo directory,
there are four build items. The item
echo-a in the a
directory uses the make backend and does not modify the
ECHO_MESSAGE variable:
plugin/echo/a/Abuild.mk
RULES := empty
The item echo-b in the
b directory uses the make backend and
modifies the ECHO_MESSAGE variabel:
plugin/echo/b/Abuild.mk
ECHO_MESSAGE += with modifications RULES := empty
The item echo-c in the
c directory uses the Groovy backend and does
not modify the echo.message parameter:
plugin/echo/c/Abuild.groovy
parameters {
abuild.rules = 'empty'
}
The item echo-d in the
d directory uses the Groovy backend and
modifies the echo.message parameter:
plugin/echo/d/Abuild.groovy
parameters {
echo.message = resolve(echo.message) + ' with modifications'
abuild.rules = 'empty'
}
To see this in action, run abuild -b desc
from the plugin/echo directory:
plugin-echo.out
abuild: build starting
abuild: echo-a (abuild-indep): all
make: Entering directory `--topdir--/plugin/echo/a/abuild-indep'
This is a message from the echoer plugin.
The value of ECHO_MESSAGE is default message
make: Leaving directory `--topdir--/plugin/echo/a/abuild-indep'
abuild: echo-b (abuild-indep): all
make: Entering directory `--topdir--/plugin/echo/b/abuild-indep'
This is a message from the echoer plugin.
The value of ECHO_MESSAGE is default message with modifications
make: Leaving directory `--topdir--/plugin/echo/b/abuild-indep'
abuild: echo-c (abuild-indep): all
[echo] This is a message from the echoer plugin.
[echo] The value of echo.message is default message
abuild: echo-d (abuild-indep): all
[echo] This is a message from the echoer plugin.
[echo] The value of echo.message is default message with modifications
abuild: build complete
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. [59] 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. Here are the
implementation file and help file:
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
plugin/plugins/printer/toolchains/abc-help.txt
The "abc" toolchain is a simple example toolchain support file. It doesn't do much of anything, but does illustrate many of the capabilities provided by abuild's ccxx rules.
You can see the help text by running abuild --help
rules toolchain:abc, and you can discover that this
toolchain is available by provided abuild --help rules
list from anywhere in the plugins
tree. To understand this file, you should read through the
comments in rules/object-code/ccxx.mk in
the abuild distribution (Appendix I, 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 rules provided
by a regular build item. Abuild provides these variables to
the make backend and also makes this
information available to the Groovy backend.
[60]
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
name: lib platform-types: native printer
plugin/other/bin/Abuild.conf
name: bin platform-types: native printer deps: lib
Here is the build output:
plugin-other-bin.out
abuild: build starting abuild: lib (abuild-<native>): all make: Entering directory `--topdir--/plugin/other/lib/abuild-<native>' This is a message from the echoer plugin. The value of ECHO_MESSAGE is default message 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 a message from the echoer plugin. The value of ECHO_MESSAGE is default message 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 a message from the echoer plugin. The value of ECHO_MESSAGE is default message 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 a message from the echoer plugin. The value of ECHO_MESSAGE is default message Compiling ../main.cc as C++ Creating program executable make: Leaving directory `--topdir--/plugin/other/bin/abuild-zzprinter.an\ \y.test-suite.abc' abuild: build complete
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:
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 tree
dependency. 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
tree dependency 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 items in other
trees. 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:
plugin-outside.out
abuild: build starting abuild: lib (abuild-<native>): all make: Entering directory `--topdir--/plugin/other/lib/abuild-<native>' This is a message from the echoer plugin. The value of ECHO_MESSAGE is default message 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 a message from the echoer plugin. The value of ECHO_MESSAGE is default message 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>' abuild: build complete
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 24.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
rules/object-code/ccxx.mk in the abuild
distribution (Appendix I, The ccxx.mk File).
In the native-compiler/outside directory,
there is another build tree that lists the plugin tree, in this
case native-compiler, as a tree dependency:
native-compiler/outside/Abuild.conf
tree-name: outside tree-deps: native-compiler name: outside 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 a tree dependency of 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:
as-needed-platforms.out
abuild: build starting 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>' abuild: build complete
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. 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
tree-name: rule-checker 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
name: item1 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:
rule-checker-item1-error.out
abuild: build starting 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>' abuild: item1 (abuild-<native>): build failed abuild: build complete abuild: ERROR: at least one build failure occurred; summary follows abuild: ERROR: build failure: item1 on platform <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
name: item2 platform-types: native deps: auto-provider
Its build proceeds normally:
rule-checker-item2-build.out
abuild: build starting 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>' abuild: build complete
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. You could even create a single project-wide global plugin that checked to make sure other plugins defined in other trees were appropriate declared, thus effectively working around the limitation of only being able to declare a single global tree dependency in a forest.
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.
[58]
We had originally wanted to call these
pre-plugin instead of
preplugin, but this interferes with the
way Groovy generates classes for scripts. Since
pre-plugin is not a valid class name
and we want to avoid specific mixed case file names (like
prePlugin, we went with
preplugin. The make version is called
the same thing for consistency.
[59]
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.
[60]
For the deprecated xml-based ant backend, corresponding ant
properties
abuild.dir.
are available.
build-item