The Abuild.groovy
file along with all rules
implementation files are loaded as scripts by the Groovy backend.
We have already discussed the parameters
closure that is available within the
Abuild.groovy
file. This closure is
provided by being included in the binding,
which is a mechanism used by Groovy to communicate with embedded
scripts. Here we discuss the remainder of the Groovy environment
used by abuild
There are three variables provided through the binding to any Groovy script that abuild loads:
abuild
An instance of the
org.abuild.groovy.BuildState
object,
which holds onto all information about the state of the
current build. This includes information about parameters,
targets, and other things as well. We discuss the interface
of this object in Section 19.10.1, “Interface to the abuild
Object”, though most
build items that don't implement any of their own rules will
find their interaction with it limited to calling
abuild.resolve
if they have any
interaction with it at all.
ant
An instance of a Groovy AntBuilder
object set up with an ant Project
specific for abuild. We discuss the ant project in more
detail in Section 19.7.2, “The Ant Project”.
parameters
A closure that provides an environment for convenient setting of parameters. We discussed this above in Section 19.2.1, “Parameter Blocks”.
Abuild creates a fresh ant project for each build item that it builds. No information is passed between build items through the ant project. The only mechanism for passing information between abuild build items is the interface system. This barrier is critical to abuild's scalability. It also results in using the same mechanism to pass information between build items regardless of whether the build items use the same backend, which is important for support true cross-language development.
The primary mechanism for passing information between a build
item and the rules used to build it is through setting and
resolving parameters, but abuild also provides some
information through ant properties. Specifically, when you
define a variable on abuild's command line, that variable
becomes available as an ant property in addition to being
visible to resolve
in abuild's parameter
blocks (or to abuild.resolve
from anywhere
in the abuild Groovy environment).
[38]
Additionally, abuild will set the project's logger and log
level based on how it was invoked, and abuild will also set
the basedir
property to the output directory
of the current build item. Note that since all Java builds are
running in one JVM, abuild cannot change the current
directory. All well-behaved ant tasks are supposed to resolve
relative paths to basedir
anyway though, so
this should generally not matter. If you find builds failing
with odd messages about missing files or directories below where
you happened to start abuild, it may be because of relative
paths being passed to incorrectly implemented ant tasks. In
this case, you can usually just prepend
${basedir}/
to the relative path you are
providing as an attribute.
Note that, although abuild makes full use of ant tasks through the ant project and the ant builder, abuild does not use ant's target facility. Instead, it defines its own with target bodies being provided by Groovy closures. This provides much greater flexibility and ease of implementation.
We have discussed how to set and resolve parameters within an
Abuild.groovy
file, but we have only just
glossed over interface variables and variable definitions passed
on abuild's command line. Most of the time, you don't have to
be concerned about the distinction, but sometimes it might be
important. If the explanation in this section doesn't make
sense to you, just skip it for now. You may never need to
understand the distinctions made here, but if something isn't
working the way you expect, you can always refer back to this
section.
Abuild's groovy backend actually maintains three separate
namespaces of variables: parameters, interface variables, and
definitions. Of these, the only ones you can actually modify
from an Abuild.groovy
file are parameters,
so any assignment made in a parameter block affects a parameter.
However, calls to resolve
have access to
interface variables and definitions. As a reminder, parameters
are set explicitly in parameters blocks. Interface variables
come from abuild's interface system and are set in
Abuild.interface
files. Definitions are
passed on abuild's command line through arguments of the form
VAR=value
. When you resolve a variable with
resolve
, here is exactly what happens:
If there is a definition of that variable that was passed on abuild's command line, return that value.
Otherwise, if there is a parameter by the name, return the value of the parameter.
Otherwise, if there is an interface variable by that name, return that value
Otherwise, return null
When you append to a parameter in a parameter block (or by
calling abuild.appendParameter
), if there
is no parameter with the name of the variable that you are
appending to, abuild will first try to initialize the
parameter by calling resolve
. This means
that if you initialize something as an interface variable and
then append to it in a parameter block,
resolve
will return a value that is the
interface variable's value appended with the changes made in
your parameter block. However, since definitions take
precedence over both interface variables and parameters, if you
specify the value of a variable on the command line, the affect
of modifying a parameter by the same name will be ignored by
future calls to resolve
. What this all
means is that variables defined on the command line effectively
override any values specified in the interface or build files.
This is equivalent to the behavior you would see with make or
ant.
[39]
If the above explanation didn't make a lot of sense, don't worry
about it. It's set up so that the Right Thing happens most of
the time without your having to worry about it.
[38] Truth be told, the primary reason for this is that the same ant project is passed to the Groovy backend as to the deprecated xml-based ant backend, for which properties are the only useful way of sending in information. However, setting these properties certainly doesn't hurt, and it might even help, so we will continue to do it.
[39]
It is possible to get at the interface, definitions, and
parameters directly through the abuild
object, but you shouldn't do it. If you are in a situation
where you are depending on that behavior, you're probably
doing something wrong.