Project specific logic

In a previous article you have seen EasyAnt basics. This does the job most of the time but who has never needed custom logic in their projects ? This usually happens when you have project specific constraints or because a feature doesn’t exist yet in your favorite build tool.

Fortunatly, easyant offers solutions for both cases. The prefered way is to create reusable plugins, however sometimes we you only need it quickly on our project and don’t have time / money / motivation to spend on reusable stuff.

Project specific logic is implemented in an ant build file module.ant by default at same level of your module.ivy file.

 <project name="my-custom-stuff">
       <echo>This message will be printed at startup </echo>
       <target name="hello">
           <echo>Hello world printed from our custom hello target</echo>
       </target>
 </project>

Invoking hello target will display our message.

 > easyant hello

Powerful isn’t it ? EasyAnt can print messages !

Ok you seem sceptical. This is useless if we can’t interact with existing targets in easyant.

Depending on an existing target

First, you can depend on existing targets or extension points.

<target name="hello2" depends="package-jar:jar">
  <echo>Hello world executed after package-jar:jar target </echo>
</target>

Invoking hello2 target will trigger package-jar:jar target and display our message. But if you invoke other target, hello2 target will never be called has no one depends on it.

This is what almost every one knows about ant. But ant provides a solution for this: Extension Point. Do you know what Extension point is ?

Definition of Extension Point

Extension-Points are similar to targets in that they have a name and a depends list and can be executed from the command line. Just like targets they represent a state during the build process.

Unlike targets they don’t contain any tasks, their main purpose is to collect targets that contribute to the desired state in their depends list.

Targets can add themselves to an extension-points’s depends list via their extensionOf attribute. The targets that add themselves will be added after the targets of the explicit depends-attribute of the extension-point, if multiple targets add themselves, their relative order is not defined.

The main purpose of an extension-point is to act as an extension point for build files designed to be imported. In the imported file an extension-point defines a state that must be reached and targets from other build files can join the depends list of said extension-point in order to contribute to that state.

Original idea was from early stages of easyant is now part of Ant since 1.8.0.

Integration of extensionPoint in EasyAnt

EasyAnt comes with a default lifecycle. This default lifecycle is a set of predefined extension points for the common needs.Default Lifecycle

Each phase is considered an essential step of the build process.

Note : Even if EasyAnt provides you a default lifecycle we never lock you in. So you’re able to add your own extension points or override existing ones.

Plugins can provide additionnal extension points. Plugins typically add low-level tasks to one or more extension-points. For example, a plugin can contribute to processing sources before compilation, you will in that case plug your own target to abstract-compile:compile-ready extension-point. This contributes to have a dynamic lifecycle.

When using easyant from the command line, you can use ProjectMan commands to get informations on available properties, targets or extensionPoints.
Invoking the following command will display available extensionPoints in your project.

> easyant -listExtensionPoints

Depending on an existing extension point

Getting back to our example could we plug our hello target to compile step ?

<target name="hello3" depends="compile">
  <echo>hello world displayed when compile extension point is reached</echo>
</target>

Calling any target depending on compile (part of the default lifecycle) will trigger hello3 target execution.

> easyant package

Will result :

...
[echo] This message will be printed at startup
...
...
compile-java:compile:
    [mkdir] Created dir: /home/neoverflow/projects/easyant/community-plugins/projects/easyant-examples/project-specific-logic/target/main/classes
    [javac] Compiling 1 source file to /home/neoverflow/projects/easyant/community-plugins/projects/easyant-examples/project-specific-logic/target/main/classes

hello3:
[echo] hello world displayed when compile extension point is reached

...
package-jar:jar:
[jar] Building jar: /home/neoverflow/projects/easyant/community-plugins/projects/easyant-examples/project-specific-logic/target/artifacts/project-specific-logic.jar

Integrating a missing feature : Code Coverage

So now we’re done with theory, we know how extension points work in Ant. I need to solve a problem specific to my project: measuring the code coverage of my tests with cobertura.

How could we integrate cobertura ant task in our module.ant file easily ?

Fetch cobertura and configure it

Usually this is done by copying manually cobertura.jar in $ANT_HOME/lib or in $USER_HOME/.ant/lib.

EasyAnt is based also on Apache Ivy therefore a quick search of cobertura on http://search.maven.org show us the ivy syntax to depend on cobertura.

<dependency org="net.sourceforge.cobertura" name="cobertura" rev="1.9.4.1"/>

But this is not a project dependency ! You don’t need it to package your project, you only need this dependency to run code coverage. Can ivy ant tasks help us here ? The answer is yes by using cachepath task. This task constructs an ant path consisting of artifacts in ivy cache for a resolved module.

Let us update our module.ant file to resolve the dependency, fill an ant classpath and configure cobertura.

<target name="cobertura:init">
     <ivy:cachepath pathid="cobertura.classpath" inline="true" organisation="net.sourceforge.cobertura" module="cobertura" revision="1.9.4.1" conf="default" settingsRef="easyant.ivy.instance"/>
     <taskdef classpathref="cobertura.classpath" resource="tasks.properties" />
</target>

 

Note the presence of settingsRef attribute. EasyAnt uses two ivy instances :

  • used to resolve/retrieve EasyAnt modules (which can be buildtypes/ plugins or skeletons) dependencies
  • used to resolve/retrieve project dependencies

There is a strong separation of context, this means that plugins dependencies will not leak in your project. Both instance are configurable.

  • Project ivy instance can be configured through an ivysettings file defined with properties. Click here if you want more details.
  • EasyAnt ivy instance can be configured through an ivysettings file defined in easyant-config. Click here if you want more details.

Perform coverage instrumentation

Now our problematic is to run coverage instrumentation, and integrate it with existing stuff in easyant.
Instrumentation needs to be done on compiled classes, so our task will depend on “compile” extension-point.

Then we could either “hardcode” some informations (path to compiled classes), or reuse easyant properties.
You can find all properties available in your project by using the following ProjectMan command :

> easyant -listProp

So here we will reuse the following properties / classpath

  • Property : target.test.classes
  • Property : target.integration.test.classes
  • Path : run.test.classpath
<target name="cobertura:instrument" depends="cobertura:init,compile,abstract-test:init">
  <property name="coverage.dir" value="${target}/coverage"/>
  <property name="coverage.datafile" value="${coverage.dir}/cobertura.ser"/>
  <mkdir dir="${coverage.dir}"/>

  <!-- delete previous coverage data file -->
  <delete file="${coverage.datafile}" />

  <!-- do instrumentation -->
  <cobertura-instrument todir="${coverage.dir}" datafile="${coverage.datafile}">
      <fileset dir="${target.test.classes}"/>
      <fileset dir="${target.test.integration.classes}"/>
  </cobertura-instrument>

  <!-- contribute to test runtime classpath and prepend instrumented classes -->
  <ea:path pathid="run.test.classpath" overwrite="prepend">
    <fileset dir="${coverage.dir}"/>
  </ea:path>
</target>

Do reporting

<target name="cobertura:run" depends="cobertura:instrument">
  <cobertura-report format="html" destdir="${target.report.dir}" srcdir="${src.main.java}" datafile="${coverage.datafile}"/>
</target>

That’s all, you’re now able to run code coverage in your project.

You can find the code of this example with project specific logic on GitHub.

Next article : We will refactor or module.ant to create a cobertura plugin.

1 Response to “Project specific logic”


  1. Writing a plugin at EasyAnt Blog

    […] « Project specific logic […]



Social Widgets powered by AB-WebLog.com.