19.7. The Abuild Groovy Environment

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

19.7.1. The Binding

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

19.7.2. The Ant Project

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.

19.7.3. Parameters, Interface Variables, and Definitions

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.