Build Configurations

Concept

Let us consider a few scenarios which make necessity for build configurations obvious.

Scenario I:

Your build is a web application. You need a quick jetty-deploy available for convenient development. Conversely, you need a javadocs plugin in your release builds but, don't want them to interfere with your development environment.

Scenario II:

You need your build to shape up differently for Linux and Windows runtimes. Maybe, you want your Windows build to package a DLL or CAB file additionally.

Build configurations are a means provided to the authors of an EasyAnt build to create common usage-patterns and publish them through the familiar module.ivy. A user can then request a profile through a CLI.

Instead of being ultra-flexible in its outlook, EasyAnt still allows common and valuable patterns to be accommodated in the same build.

Configuration Scope

All build configurations in EasyAnt are scoped at the level of a project. However, the mechanism of specifying the configuration allows you configure along multiple aspects. For example, along use-cases (dev/production/test), along target platforms (windows/linux), etc.

What aspects can one configure?

You can associate the following things with a particular build configuration:
  1. Property values
  2. Dependencies
  3. Plugins
For example, you are allowed to specify property x is available for only configuration 'conf1' and not rest. Similarly, you may specify use the 'javadocs' plugin only for 'release' configuration, and not 'dev' configuration. And, this module depends on dependency d only for configuration 'windows'.

What aspects can one configure?

Configurations are entirely defined inside the familiar module.ivy specification. EasyAnt attempts to reuse and extend the meaning of the tag of Ivy.

A typical conf tag can be converted into a profile declaration if it contains ea:type="profile" attribute. This is an instruction to EasyAnt to treat this as a Profile/Configuration.
<conf name="myconf visibility="public" description="this configuration IS USED as a build configuration" ea:type="profile"/>  
The above statement declares a new build configuration called myconf.

Attaching build elements to a configuration

Now that we have declared a build configuration, how do we define it?
A build configuration is defined by attaching properties, plugins and dependencies to it.
Here is how you may attach properties to particular configurations:


For example if you specify a property like :
<ea:build organisation="org.apache.easyant.buildtypes" module"build-std-java" revision="0.2">
<ea:property name="foo" value="bar" conf="windows"/>
<ea:property name="foo" value="foobar" conf="linux"/>
<ea:property name="goo" value="bar" conf="windows, linux"/>
<ea:plugin module="myplugin" revision="0.1" conf="windows"/>
</ea:build>
As is self-evident, the property foo will assume different values for 'windows' and 'linux' configurations. On the otherhand, declaration of 'goo' attaches itself to both windows and linux profiles with the same value. Similarly, the plugin myplugin gets attached to the 'windows' configuration only but, is not available in the linux configuration.

Activating a Configuration

Build configurations in EasyAnt are activated through Command Line in two ways:
> easyant -buildconf myconfiguration
Or alternatively :
> easyant -C myconfiguration
Any of the above commands would trigger the build process using myconfiguration build configuration.

A Practical Use Case

This section tries to illustrate the technical details of Build Configurations. We will try to setup a web application build and then configure it for three different target environments, viz. Release, Testing and Development.

Of these, release and testing configurations require to publish coverage reports. The development configuration focuses on making developers life easy. So, it needs a quick jetty deploy functionality available in the build.

Let us try to set up a simply EasyAnt build with the above intention in mind.

Example:

<configurations>
<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf"/>
<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal
use of the application, and is only available for the test compilation and execution phases."/>
<conf name="release" visibility="public" ea:type="profile" description="Declares a build configuration for release builds"/>
<conf name="testing" visibility="public" ea:type="profile" description="Declares a build configuration for testing builds"/>
<conf name="dev" visibility="public" ea:type="profile" description="Declares a build configuration for dev builds"/>
</configurations>
The above module.ivy snippet declares the three build configurations, viz. 'release', 'testing' and 'dev'. Now, we need to attach our requirement dependencies to each of these configurations.

So, we modify our tag as:

<ea:build organisation="org.apache.easyant.buildtypes" module="build-webapp-java" revision="0.1">
<ea:property name="test.framework" value="testng"/>
<ea:property name="run.main.classname" value="org.apache.easyant.example.Example" conf="dev"/>
<ea:plugin module="jetty-deploy" revision="0.1" as="jetty" conf="dev"/>
<ea:plugin module="run-java revision="0.1" as="run-java" conf="dev"/>
<ea:plugin module="emma" revision="0.1" as="emma" conf="release,testing"/>
</ea:build>
In the above snippet, jetty-deploy, run-java and emma plugins have been declared and attached to different build configurations.

While jetty-deploy and run-java plugins have been attached to dev configuration, the emma plugin has been attached to both testing and release configurations. The jetty-deploy plugin allows a quick deployment of the webapp build on a jetty server. The run-java plugin allows you to execute any java class. Let us assume our dev environment requires us to execute a java class. So we include this dependency to our dev environment. Further, the run-java plugin requires the class name to be executed through the property run.main.classname. So, we additionally define this property and attach it to the dev configuration as well.

On the other hand, for our release builds, generation of coverage reports is a process requirement. And in testing, coverage reports are an obvious requirement. So we attach the emma plugin to both these configurations.

So, this is how our final module.ivy looks:

<ivy-module version="2.0" xmlns:e="http://ant.apache.org/ivy/extra"
xmlns:ea="http://www.easyant.org">
<info organisation="org.apache.easyant" module="webapp-java"
status="integration">
<description>
This project is a sample of a java webapplication making
use of build configurations
</description>
<ea:build organisation="org.apache.easyant.buildtypes" module="build-webapp-java" revision="0.1">
<ea:property name="test.framework" value="testng" />
<ea:property name="run.main.classname" value="org.apache.easyant.example.Example"
conf="dev" />
<ea:plugin module="jetty-deploy" revision="0.1"
as="jetty" conf="dev" />
<ea:plugin module="run-java" revision="0.1"
as="run-java" conf="dev" />
<ea:plugin module="emma" revision="0.1" as="emma"
conf="release,testing" />
</ea:build>
</info>
<configurations>
<conf name="default" visibility="public"
description="runtime dependencies and master artifact can be used with this conf" />
<conf name="test" visibility="private"
description="this scope indicates that the dependency is not required for normal
use of the application, and is only available for the test compilation and execution phases." />
<conf name="release" visibility="public" ea:type="profile"
description="Declares a build configuration for official builds" />
<conf name="testing" visibility="public" ea:type="profile"
description="Declares a build configuration for testing builds" />
<conf name="dev" visibility="public" ea:type="profile"
description="Declares a build configuration for dev builds" />
</configurations>
<publications>
<artifact type="war" />
<artifact e:classifier="source" />
</publications>
<dependencies>
<dependency org="org.testng" name="testng" rev="5.7"
conf="test->master">
<artifact name="testng" type="jar" e:classifier="jdk15" />
</dependency>
</dependencies>
</ivy-module>

Running different configurations/profiles

With the above module.ivy, you can go ahead and run the build against any of the configuration. For example, if you are a developer, trying to deploy and test your changes on jetty, you would like to run:
> easyant -C dev jetty:run
> easyant -C dev run-java:run
The first command will build and package your app, and deploy it on a jetty instance. The second will run the ${run.main.classname} class in a new JVM.

Or, try out this -

> easyant -C testing emma:emma
> easyant -C release emma:emma
This would generate a emma coverage report for your project.