http://javabeanz.wordpress.com/2010/10/13/how-to-get-timestamp-in-ant-scripts/
Showing posts with label ant. Show all posts
Showing posts with label ant. Show all posts
Monday, 20 June 2011
Friday, 15 April 2011
Introduction to Apache Maven: A build framework & build automation tool
For a quite while I had been using Ant build tool for building my Java projects. Since few days I have been switching to Apache Maven, an open source build framework that can be used to build almost all the projects in Java, .Net and other platforms. Maven was created by Sonatype’s Jason van Zyl in 2002.

Maven 2.0 is based around the central concept of a build lifecycle. What this means is that the process for building and distributing a particular artifact (project) is clearly defined.
A Build Lifecycle is Made Up of Phases. Each of these build lifecycles is defined by a different list of build phases, wherein a build phase represents a stage in the lifecycle.

Dependency management is one of the features of Maven that is best known to users and is one of the areas where Maven excels. There is not much difficulty in managing dependencies for a single a project, but when you start getting into dealing with multi-module projects and applications that consist of tens or hundreds of modules this is where Maven can help you a great deal in maintaining a high degree of control and stability.

Maven helps you to generate documentation of your project and create a Project Site. Not only does it generates the site code, but it can deploy the website on a server. Maven has several reports that you can add to your web site to display the current state of the project. These reports take the form of plugins, just like those used to build the project.
Internationalization in Maven is very simple, as long as the reports you are using have that particular locale defined.
The POM was renamed from project.xml in Maven 1 to pom.xml in Maven 2. Instead of having a maven.xml file that contains the goals that can be executed, the goals or plugins are now configured in the pom.xml. When executing a task or goal, Maven looks for the POM in the current directory. It reads the POM, gets the needed configuration information, then executes the goal.
* project – root
* modelVersion – should be set to 4.0.0
* groupId – the id of the project’s group.
* artifactId – the id of the artifact (project)
* version – the version of the artifact under the specified group
* Per User – Defined in the Maven-settings (%USER_HOME%/.m2/settings.xml).
* Global – Defined in the global maven-settings (%M2_HOME%/conf/settings.xml).
* Profile descriptor – a descriptor located in project basedir (profiles.xml)
A repository in Maven is used to hold build artifacts and dependencies of varying types. There are strictly only two types of repositories: local and remote. The local repository refers to a copy on your own installation that is a cache of the remote downloads, and also contains the temporary build artifacts that you have not yet released. The local and remote repositories are structured the same way so that scripts can easily be run on either side, or they can be synced for offline used. In general use, the layout of the repositories is completely transparent to the Maven user, however.
To override this, you need to specify a repositories element as follows:
Plugins are the central feature of Maven that allow for the reuse of common build logic across multiple projects. They do this by executing an “action” (i.e. creating a WAR file or compiling unit tests) in the context of a project’s description – the Project Object Model (POM). Plugin behavior can be customized through a set of unique parameters which are exposed by a description of each plugin goal (or Mojo).
What is Maven?
Maven, a Yiddish word meaning accumulator of knowledge, was originally started as an attempt to simplify the build processes in the Jakarta Turbine project. Maven is really a process of applying patterns to a build infrastructure in order to provide a coherent view of software projects.Maven is a build tool
Maven 2.0 is based around the central concept of a build lifecycle. What this means is that the process for building and distributing a particular artifact (project) is clearly defined.
A Build Lifecycle is Made Up of Phases. Each of these build lifecycles is defined by a different list of build phases, wherein a build phase represents a stage in the lifecycle.
Maven is dependency management tool
Dependency management is one of the features of Maven that is best known to users and is one of the areas where Maven excels. There is not much difficulty in managing dependencies for a single a project, but when you start getting into dealing with multi-module projects and applications that consist of tens or hundreds of modules this is where Maven can help you a great deal in maintaining a high degree of control and stability.
A Documentation tool
Maven helps you to generate documentation of your project and create a Project Site. Not only does it generates the site code, but it can deploy the website on a server. Maven has several reports that you can add to your web site to display the current state of the project. These reports take the form of plugins, just like those used to build the project.
Internationalization in Maven is very simple, as long as the reports you are using have that particular locale defined.
Objectives of Maven
- Make the development process visible or transparent
- Provide an easy way to see the health and status of a project
- Decreasing training time for new developers
- Bringing together the tools required in a uniform way
- Preventing inconsistent setups
- Providing a standard development infrastructure across projects
- Focus energy on writing applications
Project Object Model (pom.xml)
A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details used by Maven to build the project. It contains default values for most projects. Examples for this is the build directory, which is target; the source directory, which is src/main/java; the test source directory, which is src/main/test; and so on.The POM was renamed from project.xml in Maven 1 to pom.xml in Maven 2. Instead of having a maven.xml file that contains the goals that can be executed, the goals or plugins are now configured in the pom.xml. When executing a task or goal, Maven looks for the POM in the current directory. It reads the POM, gets the needed configuration information, then executes the goal.
POM Sample
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-core-api-container</artifactId>
<name>Cargo Core Container API</name>
<version>0.7-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies/>
<build/>
[...]
* project – root
* modelVersion – should be set to 4.0.0
* groupId – the id of the project’s group.
* artifactId – the id of the artifact (project)
* version – the version of the artifact under the specified group
Build Profiles in Maven
Profiles are specified using a subset of the elements available in the POM itself (plus one extra section), and are triggered in any of a variety of ways. They modify the POM at build time, and are meant to be used in complementary sets to give equivalent-but-different parameters for a set of target environments (providing, for example, the path of the appserver root in the development, testing, and production environments). As such, profiles can easily lead to differing build results from different members of your team. However, used properly, profiles can be used while still preserving project portability. This will also minimize the use of -f option of maven which allows user to create another POM with different parameters or configuration to build which makes it more maintainable since it is runnning with one POM only.Different types of Profile
* Per Project – Defined in the POM itself (pom.xml).* Per User – Defined in the Maven-settings (%USER_HOME%/.m2/settings.xml).
* Global – Defined in the global maven-settings (%M2_HOME%/conf/settings.xml).
* Profile descriptor – a descriptor located in project basedir (profiles.xml)
Repositories in Maven
Declaring a Remote Repository
Downloading in Maven is triggered by a project declaring a dependency that is not present in the local repository (or for a SNAPSHOT, when the remote repository contains one that is newer). By default, Maven will download from the central repository (http://repo1.maven.org/maven2/).To override this, you need to specify a repositories element as follows:
<project>
...
<repositories>
<repository>
<id>my-internal-site</id>
<url>http://myserver/repo</url>
</repository>
</repositories>
...
</project>
Plugins in Maven
Maven is really just a core framework for a collection of Maven Plugins. In other words, plugins are where much of the real action is performed, plugins are used to: create jar files, create war files, compile code, unit test code, create project documentation, and on and on. Almost any action that you can think of performing on a project is implemented as a Maven plugin.Plugins are the central feature of Maven that allow for the reuse of common build logic across multiple projects. They do this by executing an “action” (i.e. creating a WAR file or compiling unit tests) in the context of a project’s description – the Project Object Model (POM). Plugin behavior can be customized through a set of unique parameters which are exposed by a description of each plugin goal (or Mojo).
Monday, 7 February 2011
Building an executable jar with Ant
An executable jar file has a defined default class, allowing the default class to be invoked without explicitly identifying it via the command line. On a supporting OS (such as Mac OS/X or Microsoft Windows), this allows the class to be invoked by double-clicking the file.
I know that making a jar file executable requires an entry in the manifest file. I can usually remember that the attribute in question is Main-Class (although I'm usually fuzzy about the proper capitalization). The problem is remembering the correct command line options to insert my own MANIFEST.MF file into a jar file.
All in all, it is much simpler to just use ant to handle it for me. Let's start with the example from my Ant Hello World Revisited. The new ant build file follows, with the new lines shown in bold. Note that I've also designated the "jar" task as the default task.
I know that making a jar file executable requires an entry in the manifest file. I can usually remember that the attribute in question is Main-Class (although I'm usually fuzzy about the proper capitalization). The problem is remembering the correct command line options to insert my own MANIFEST.MF file into a jar file.
All in all, it is much simpler to just use ant to handle it for me. Let's start with the example from my Ant Hello World Revisited. The new ant build file follows, with the new lines shown in bold. Note that I've also designated the "jar" task as the default task.
$ cat hello.xml
<project default="jar">
<target name="compile">
<javac srcdir="." />
</target>
<target name="jar" depends="compile">
<jar destfile="hello.jar"
basedir="."
includes="**/*.class">
<manifest>
<attribute name="Main-Class" value="hello" />
</manifest>
</jar>
</target>
</project>
$ cat hello.java
public class hello {
public static void main( String[] args )
{
System.out.println( "Hello World" );
}
}Now we simply build our jar file:
$ rm *.jar
$ ant -f hello.xml
Buildfile: hello.xml
compile:
jar:
[jar] Building jar: /Tutorial/Ant/Jar/hello.jar
BUILD SUCCESSFUL
Total time: 2 secondsAnd execute the default class by using the -jar command line option to java:
$ java -jar hello.jar
Hello WorldUsing Ant Properties
In this Ant tutorial installment, we will use ant Properties to specify the work directory structure that will contain our .class and .jar files. The work directory structure tends to be a personal preference, and properties will allow us to centralize the structure definition so that any change will be relatively pain-free. We specify the directories with a location attribute, thereby binding the property to a file system location. This allows us to pass the property to another ant process without any local directory side effects.
<project default="all">
<property name="obj-dir" location="obj" />
<property name="lib-dir" location="lib" />
<target name="init">
<mkdir dir="${obj-dir}" />
<mkdir dir="${lib-dir}" />
</target>
<target name="clean-init">
<delete dir="${obj-dir}" />
<delete dir="${lib-dir}" />
</target>
<target name="all" depends="init"/>
<target name="clean" depends="clean-init"/>
</project>This build file features: - 4 targets:
- init: to build the work directory structure.
- clean-init: to remove the work directory structure.
- all: the build roll-up target.
- clean: to clean roll-up target. Note that my target naming convention is to pair a target with a clean-target. I reserve the all and the clean targets as roll-up targets for all build and clean targets respectively.
- 2 properties:
- obj-dir: the root directory for our .class files.
- lib-dir: the root directory for our .jar files.
$ ls
build.xml
$ ant
Buildfile: build.xml
init:
[mkdir] Created dir: /Tutorial/Ant/Properties/obj
[mkdir] Created dir: /Tutorial/Ant/Properties/lib
all:
BUILD SUCCESSFUL
Total time: 3 seconds
$ ls
build.xml obj/ lib/
$ ant clean
Buildfile: build.xml
clean-init:
[delete] Deleting directory /Tutorial/Ant/Properties/obj
[delete] Deleting directory /Tutorial/Ant/Properties/lib
clean:
BUILD SUCCESSFUL
Total time: 2 seconds
$ ls
build.xmlNow, let's add targets to compile, jar and execute a java class. We'll just copy over the hello.java class from our previous installment and place it in the src directory. The compile and jar targets from before remain largely the same; but we add the destdir attribute to the javac task to specify where the .class files are to be stored and we can remove the includes attribute from the jar task. Our new build.xml looks like this:
<project default="all">
<property name="obj-dir" location="obj" />
<property name="lib-dir" location="lib" />
<property name="src-dir" location="src" />
<target name="init">
<mkdir dir="${obj-dir}" />
<mkdir dir="${lib-dir}" />
</target>
<target name="clean-init">
<delete dir="${obj-dir}" />
<delete dir="${lib-dir}" />
</target>
<target name="compile" depends="init">
<javac srcdir="${src-dir}"
destdir="${obj-dir}"
/>
</target>
<target name="clean-compile">
<delete>
<fileset dir="${obj-dir}" includes="**/*.class" />
</delete>
</target>
<target name="jar" depends="compile">
<jar destfile="${lib-dir}/hello.jar"
basedir="${obj-dir}"
/>
</target>
<target name="clean-jar">
<delete file="${lib-dir}/hello.jar" />
</target>
<target name="run" depends="jar">
<java classname="hello"
classpath="${lib-dir}/hello.jar"
fork="true"
/>
</target>
<target name="all" depends="run"/>
<target name="clean" depends="clean-init"/>
</project>And now we can execute our class and clean up our work directories.
$ ant run
Buildfile: build.xml
init:
[mkdir] Created dir: /Tutorial/Ant/Properties/obj
[mkdir] Created dir: /Tutorial/Ant/Properties/lib
compile:
[javac] Compiling 1 source file to /Tutorial/Ant/Properties/obj
jar:
[jar] Building jar: /Tutorial/Ant/Properties/lib/hello.jar
run:
[java] Hello World
BUILD SUCCESSFUL
Total time: 4 seconds
$ ant clean
Buildfile: build.xml
clean-init:
[delete] Deleting directory /Tutorial/Ant/Properties/obj
[delete] Deleting directory /Tutorial/Ant/Properties/lib
clean:
BUILD SUCCESSFUL
Total time: 2 secondsAnt and JUnit
In this Ant tutorial installment, we'll take a look at the JUnit task in ant. The JUnit task is moderately complex, so you'll definitely want to read the Ant manual. I would also recommend taking a look at Java Development with Ant by Erik Hatcher and Steve Loughran (this link will allow you to download Chapter 4: Testing with JUnit). to complete your installation of JUnit. Finally, this example was developed using version 1.5.1 of Ant and version 3.8.1 of JUnit.
We'll start with the test class shown below. For simplicity, we'll use a test case that always succeeds and doesn't really test anything. We'll take a closer look at writing JUnit test cases later. For now we'll just say that a JUnit test case extends the junit.framework.TestCase class. Please create this file in a sub-directory named test (or change the value of the build file tst-dir property to suit).
We'll start with the test class shown below. For simplicity, we'll use a test case that always succeeds and doesn't really test anything. We'll take a closer look at writing JUnit test cases later. For now we'll just say that a JUnit test case extends the junit.framework.TestCase class. Please create this file in a sub-directory named test (or change the value of the build file tst-dir property to suit).
import junit.framework.*;
public class TestExample extends TestCase {
public void testOne()
{
assertTrue( "TestExample", true );
}
}And here is the build.xml file that we'll be using. Please modify this build file to reflect the location of junit.jar on your system. In addition, you must add junit.jar to your CLASSPATH or copy junit.jar to your Ant library from your JUnit library to enable the integration of JUnit and Ant.
<project default="all">
<property name="tst-dir" location="test" />
<property name="TALK" value="true" />
<path id="classpath.base">
</path>
<path id="classpath.test">
<pathelement location="/Projects/Java/Lib/junit.jar" />
<pathelement location="${tst-dir}" />
<path refid="classpath.base" />
</path>
<target name="compile-test">
<javac srcdir="${tst-dir}"
verbose="${TALK}"
>
<classpath refid="classpath.test"/>
</javac>
</target>
<target name="clean-compile-test">
<delete verbose="${TALK}">
<fileset dir="${tst-dir}" includes="**/*.class" />
</delete>
</target>
<target name="test" depends="compile-test">
<junit>
<classpath refid="classpath.test" />
<formatter type="brief" usefile="false" />
<test name="TestExample" />
</junit>
</target>
<target name="all" depends="test" />
<target name="clean" depends="clean-compile-test" />
</project>There are three primary differences between this build.xml file and the ones used previously: - Addition of the TALK property. We'll use this as a flag to the verbose attribute of various tasks. Our example below will demonstrate the effect of the verbose attribute for the delete and the javac tasks. Note that we assign the property with the value attribute since this is not a file system location.
This is different from the ant -verbose option. Setting verbose=true on a task causes that task to be more informative about it's behavior. Passing the -verbose option to ant causes ant to be more informative about ant's behavior. - Explicit declaration of the Java Classpath. In this case we define a base classpath and then we define the test classpath relative to it. If we were actually testing a real class, then the base classpath would be used to compile that class. The test classpath adds the location of the JUnit tests and the JUnit jar file to the base classpath.
- The JUnit task. There are three nested elements to our JUnit task:
- The java classpath.
- The formatter, which controls the output from the task.
- The name, which defines a single test class to be run.
$ ant -DTALK=false clean
Buildfile: build.xml
clean-compile-test:
[delete] Deleting 1 files from /Tutorial/Ant/Junit/test
clean:
BUILD SUCCESSFUL
Total time: 2 seconds
$ ant -DTALK=false
Buildfile: build.xml
compile-test:
[javac] Compiling 1 source file
test:
[junit] Testsuite: TestExample
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.007 sec
all:
BUILD SUCCESSFUL
Total time: 4 seconds
$ ant clean
Buildfile: build.xml
clean-compile-test:
[delete] Deleting 1 files from /Tutorial/Ant/Junit/test
[delete] Deleting /Tutorial/Ant/Junit/test/TestExample.class
clean:
BUILD SUCCESSFUL
Total time: 2 seconds
$ ant
Buildfile: build.xml
compile-test:
[javac] Compiling 1 source file
[javac] [parsing started /Tutorial/Ant/Junit/test/TestExample.java]
[javac] [parsing completed 81ms]
[javac] [loading /Projects/Java/Lib/junit.jar(junit/framework/TestCase.class)]
[javac] [checking TestExample]
[javac] [loading /Projects/Java/Lib/junit.jar(junit/framework/Assert.class)]
[javac] [loading /System/Library/.../1.3.1/Classes/classes.jar(java/lang/Object.class)]
[javac] [loading /Projects/Java/Lib/junit.jar(junit/framework/Test.class)]
[javac] [loading /System/Library/.../1.3.1/Classes/classes.jar(java/lang/String.class)]
[javac] [wrote /Tutorial/Ant/Junit/test/TestExample.class]
[javac] [total 866ms]
test:
[junit] Testsuite: TestExample
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.005 sec
all:
BUILD SUCCESSFUL
Total time: 4 secondsAnt and multiple JUnit test cases
In this Ant tutorial installment, we'll use the JUnit task to execute multiple test classes. Our starting point will be the trivial JUnit task presented previously. This example was developed using version 1.5.1 of Ant and version 3.8.1 of JUnit.
For simplicity, we'll add the additional JUnit test class shown below. This class uses the assertEquals method with arguments that will always cause an AssertionFailedError. Please place this file in a sub-directory named test as before.
For simplicity, we'll add the additional JUnit test class shown below. This class uses the assertEquals method with arguments that will always cause an AssertionFailedError. Please place this file in a sub-directory named test as before.
import junit.framework.*;
public class Test extends TestCase {
public void test()
{
assertEquals( "Equality Test", 0, 1 );
}
}And here is the build.xml file that we'll be using. There are just two changes from our previous example (shown in bold). First, we set property TALK to false in order to decrease the amount of output. And second, we replace the single test sub-element of the JUnit task with a batchtest that uses a file system regular expression to identify the test classes to run.
<project default="all">
<property name="tst-dir" location="test" />
<property name="TALK" value="false" />
<path id="classpath.base">
</path>
<path id="classpath.test">
<pathelement location="/Projects/Java/Lib/junit.jar" />
<pathelement location="${tst-dir}" />
<path refid="classpath.base" />
</path>
<target name="compile-test">
<javac srcdir="${tst-dir}"
verbose="${TALK}"
>
<classpath refid="classpath.test"/>
</javac>
</target>
<target name="clean-compile-test">
<delete verbose="${TALK}">
<fileset dir="${tst-dir}" includes="**/*.class" />
</delete>
</target>
<target name="test" depends="compile-test">
<junit>
<classpath refid="classpath.test" />
<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${tst-dir}" includes="**/Test*.class" />
</batchtest>
</junit>
</target>
<target name="all" depends="test" />
<target name="clean" depends="clean-compile-test" />
</project>We can now execute our test.
$ ant clean
Buildfile: build.xml
clean-compile-test:
[delete] Deleting 2 files from /Tutorial/Ant/JUnit2/test
clean:
BUILD SUCCESSFUL
Total time: 4 seconds
$ ant test
Buildfile: build.xml
compile-test:
[javac] Compiling 2 source files
test:
[junit] Testsuite: Test
[junit] Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 0.012 sec
[junit] Testcase: test(Test): FAILED
[junit] Equality Test expected:<0> but was:<1>
[junit] junit.framework.AssertionFailedError: Equality Test expected:<0> but was:<1>
[junit] at Test.test(Unknown Source)
[junit] TEST Test FAILED
[junit] Testsuite: TestExample
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.001 sec
BUILD SUCCESSFUL
Total time: 5 secondsNote that ant exits with BUILD SUCCESSFUL despite a junit failure. This was an intentional choice by the designers to allow the completion of a test suite. A revised target that includes the recommended JUnit task structure is shown below. In this structure, a property is set when the JUnit task encounters a failure (test failure or JUnit error). After the JUnit task completes, we fail the target if the failureProperty was set.
<target name="test" depends="compile-test">
<junit failureProperty="test.failure" >
<classpath refid="classpath.test" />
<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${tst-dir}" includes="**/Test*.class" />
</batchtest>
</junit>
<fail message="test failed" if="test.failure" />
</target>We'll also modify the compile-test target by setting debug="true" in the javac task as shown. This will add line number information to the errors reported from JUnit.
<target name="compile-test">
<javac srcdir="${tst-dir}"
verbose="${TALK}"
debug="true"
>
<classpath refid="classpath.test"/>
</javac>
</target>Now we execute ant:
$ ant clean
Buildfile: build.xml
clean-compile-test:
[delete] Deleting 2 files from /Tutorial/Ant/JUnit2/test
clean:
BUILD SUCCESSFUL
Total time: 3 seconds
$ ant test
Buildfile: build.xml
compile-test:
[javac] Compiling 2 source files
test:
[junit] Testsuite: Test
[junit] Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 0.009 sec
[junit] Testcase: test(Test): FAILED
[junit] Equality Test expected:<0> but was:<1>
[junit] junit.framework.AssertionFailedError: Equality Test expected:<0> but was:<1>
[junit] at Test.test(Test.java:7)
[junit] TEST Test FAILED
[junit] Testsuite: TestExample
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0 sec
BUILD FAILED
file:/Tutorial/Ant/JUnit2/build.xml:36: test failed
Total time: 4 secondsMuch better. Here's the full build.xml file:
<project default="all">
<property name="tst-dir" value="test" />
<property name="TALK" value="false" />
<path id="classpath.base">
</path>
<path id="classpath.test">
<pathelement location="/Projects/Java/Lib/junit.jar" />
<pathelement location="${tst-dir}" />
<path refid="classpath.base" />
</path>
<target name="compile-test">
<javac srcdir="${tst-dir}"
verbose="${TALK}"
debug="true"
>
<classpath refid="classpath.test"/>
</javac>
</target>
<target name="clean-compile-test">
<delete verbose="${TALK}">
<fileset dir="${tst-dir}" includes="**/*.class" />
</delete>
</target>
<target name="test" depends="compile-test">
<junit failureProperty="test.failure">
<classpath refid="classpath.test" />
<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${tst-dir}" includes="**/Test*.class" />
</batchtest>
</junit>
<fail message="test failed" if="test.failure" />
</target>
<target name="all" depends="test" />
<target name="clean" depends="clean-compile-test" />
</project>Conditional Execution in Ant
In this Ant tutorial installment, we'll take a look at the conditional execution of targets. Let's start with the simple Hello World build file from before. Modify the hello and goodbye targets to include the if and unless attributes as shown in bold.
<project default="all">
<target name="hello" if="hello.set">
<echo message="Hello, World"/>
</target>
<target name="goodbye" unless="hello.set">
<echo message="Goodbye, Cruel World"/>
</target>
<target name="all" depends="hello,goodbye" />
</project>The if="named.property" attribute causes a target to be executed if the named property is set. And the unless="named.property" attribute causes a target to be executed unless the named property is set. If we execute the build file without specifying a value for the property "hello.set", then the hello target will be skipped and the goodbye target will be executed. And specifying a value for the property "hello.set" yields the opposite result.
$ ant
Buildfile: build.xml
hello:
goodbye:
[echo] Goodbye, Cruel World
all:
BUILD SUCCESSFUL
Total time: 2 seconds
$ ant -Dhello.set=true
Buildfile: build.xml
hello:
[echo] Hello, World
goodbye:
all:
BUILD SUCCESSFUL
Total time: 4 secondsOf course, we could just specify the desired target on the command line and avoid all that. But let's take a look at how we can use this to cleanup after a failed target. Start with the build file we used to run multiple JUnit test cases and split the original target "test" into three pieces: test-execute, test-failed, and test. Target "test-execute" will contain the JUnit task, target "test-failed" will contain the cleanup code and be conditional on if="test.failure", and target "test" will tie it all together. This will execute our clean up code if a test fails and skip the clean up code if all tests pass. Note that we could consolidate targets "test-failed" and "test" together at the cost of some clarity about the intent of each target.
<project default="all">
<property name="tst-dir" value="test" />
<property name="TALK" value="false" />
<path id="classpath.base">
</path>
<path id="classpath.test">
<pathelement location="/Projects/Java/Lib/junit.jar" />
<pathelement location="${tst-dir}" />
<path refid="classpath.base" />
</path>
<target name="compile-test">
<javac srcdir="${tst-dir}"
verbose="${TALK}"
debug="true"
>
<classpath refid="classpath.test"/>
</javac>
</target>
<target name="clean-compile-test">
<delete verbose="${TALK}">
<fileset dir="${tst-dir}" includes="**/*.class" />
</delete>
</target>
<target name="test-execute" depends="compile-test">
<junit failureProperty="test.failure">
<classpath refid="classpath.test" />
<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${tst-dir}" includes="**/Test*.class" />
</batchtest>
</junit>
<fail message="test failed" if="test.failure" />
</target>
<target name="test-failed" depends="test-execute" if="test.failure">
<echo message="perform test cleanup" />
<fail message="test failed" />
</target>
<target name="test" depends="test-failed" />
<target name="all" depends="test" />
<target name="clean" depends="clean-compile-test" />
</project>Execution with Test Failure
$ ant -f build.xml
Buildfile: build.xml
compile-test:
test-execute:
[junit] Testsuite: Test
[junit] Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 0.035 sec
[junit] Testcase: test(Test): FAILED
[junit] Equality Test expected:<0> but was:<1>
[junit] junit.framework.AssertionFailedError: Equality Test expected:<0> but was:<1>
[junit] at Test.test(Test.java:7)
[junit] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[junit] at sun.reflect.NativeMethodAccessorImpl.invoke( ... .java:39)
[junit] at sun.reflect.DelegatingMethodAccessorImpl.invoke( ... .java:25)
[junit] TEST Test FAILED
[junit] Testsuite: TestExample
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.001 sec
test-failed:
[echo] perform test cleanup
BUILD FAILED
file:/Tutorial/Ant/Conditional/junit.xml:38: test failed
Total time: 3 secondsExecution with Test Success
$ ant -f build.xml
Buildfile: build.xml
compile-test:
test-execute:
[junit] Testsuite: Test
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.026 sec
[junit] Testsuite: TestExample
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.001 sec
test-failed:
test:
all:
BUILD SUCCESSFUL
Total time: 4 secondsSetting Java System Properties with Ant
In this installment, we'll learn how to set a Java System Property with ant. Here is our Java class. It simply accesses the System Properties object and prints the value of the test.property key.
import java.util.Properties;
public class prop {
public static void main( String[] args )
{
String testProp = "test.property";
Properties sysProps = System.getProperties();
System.out.println( "Value of " + testProp + " is " +
sysProps.getProperty(testProp) );
}
}And here is the simple Ant build.xml that compiles and executes that class.
<project default="run">
<target name="compile">
<javac srcdir="." />
</target>
<target name="run" depends="compile">
<java classname="prop"
fork="true">
</java>
</target>
</project>With the following result:
$ ant run
Buildfile: build.xml
compile:
[javac] Compiling 1 source file
run:
[java] Value of test.property is null
BUILD SUCCESSFUL
Total time: 6 secondsTo specify a System Property we use the sysproperty attribute of the java task. We'll change the run target to assign the value blue to the key test.property
<target name="run" depends="compile">
<java classname="prop"
fork="true">
<sysproperty key="test.property"
value="blue"
/>
</java>
</target>And we get the expected output on execution.
$ ant
Buildfile: build.xml
compile:
run:
[java] Value of test.property is blue
BUILD SUCCESSFUL
Total time: 3 secondsSo far, so good. Of course, if test.property is going to have a fixed value, then we might as well set it from within our application. If test.property will typically have a wide range of simple values, then it is probably best managed as a parameter with a default value that can be overridden from the command line.
<project default="run">
<target name="compile">
<javac srcdir="." />
</target>
<property name="COLOR" value="red" />
<target name="run" depends="compile">
<java classname="prop"
fork="true">
<sysproperty key="test.property"
value="${COLOR}"
/>
</java>
</target>
</project>And if the property has a small number of complex values, then it can be set based upon another parameter that can be set from the command line.
<project default="run">
<target name="compile">
<javac srcdir="." />
</target>
<property name="COLOR" value="red" />
<target name="setup-run">
<condition property="COMPLEX-COLOR"
value="complex.property.when.red.">
<contains string="red" substring="${COLOR}" casesensitive="false" />
</condition>
<condition property="COMPLEX-COLOR"
value="complex.property.when.blue.">
<contains string="blue" substring="${COLOR}" casesensitive="false" />
</condition>
<condition property="COMPLEX-COLOR"
value="complex.property.when.unknown.">
<not>
<isset property="COMPLEX-COLOR" />
</not>
</condition>
</target>
<target name="run" depends="compile,setup-run">
<java classname="prop"
fork="true">
<sysproperty key="test.property"
value="${COMPLEX-COLOR}"
/>
</java>
</target>
</project>Which let's us control the property from the command line:
$ ant
Buildfile: build.xml
compile:
setup-run:
run:
[java] Value of test.property is complex.property.when.red.
BUILD SUCCESSFUL
Total time: 3 seconds
$ ant -DCOLOR=BLue
Buildfile: build.xml
compile:
setup-run:
run:
[java] Value of test.property is complex.property.when.blue.
BUILD SUCCESSFUL
Total time: 3 seconds
$ ant -DCOLOR=purple
Buildfile: build.xml
compile:
setup-run:
run:
[java] Value of test.property is complex.property.when.unknown.
BUILD SUCCESSFUL
Total time: 3 secondsSetting Properties with Ant Tasks
In this Ant tutorial installment, we'll take a closer look at setting properties. The combination of properties and conditional execution allows more robust build files that adapt to the computing environment. Start with this basic build.xml file.
<project default="set">
<target name="set">
<property name="status" value="false"/>
<echo message="the status is ${status}" />
</target>
<property name="status" value="true"/>
</project>And then execute ant.
$ ant
Buildfile: build.xml
set:
[echo] the status is true
BUILD SUCCESSFUL
Total time: 2 secondsThis demonstrates two characteristics of the property task: - Properties are immutable. Once they are set, they stay set (with some inevitable exceptions). If properties were mutable, then the property assignment task
<property name="status" value="false"/> would result in the message the status is false. - Property tasks that are direct children of the project are executed before any target task is executed. In this case, the property assignment task
<property name="status" value="true"/> is a direct child of the project and is executed before the target set.
<project default="all">
<target name="init">
<available property="present"
file="foo"
/>
</target>
<target name="exists" depends="init" if="present">
<echo message="foo exists" />
</target>
<target name="all" depends="exists" />
</project>Execute this build.xml file twice. Once with an empty directory and once after creating file foo with the touch command.
$ ls
build.xml
$ ant
Buildfile: build.xml
init:
exists:
all:
BUILD SUCCESSFUL
Total time: 3 seconds
$ touch foo
$ ant
Buildfile: build.xml
init:
exists:
[echo] foo exists
all:
BUILD SUCCESSFUL
Total time: 3 secondsIn the previous example, we only checked for the existence of foo. In this example, we will also identify whether it is a directory or a file. The available task's type="dir" attribute specifically tests for a directory and is used to set property isaDir. We could use the type="file" attribute to set property isaFile but we'll use the condition task instead. The condition task is the generalized method for setting parameters. Please see the manual for a complete list of the supported conditions.
<project default="all">
<target name="init">
<available property="present"
file="foo"
/>
<available property="isaDir"
file="foo"
type="dir"
/>
<condition property="isaFile">
<and>
<isset property="present" />
<not>
<isset property="isaDir" />
</not>
</and>
</condition>
</target>
<target name="exists" depends="init" if="present">
<echo message="foo exists" />
</target>
<target name="isFile" depends="init" if="isaFile">
<echo message="foo is is a file" />
</target>
<target name="isDir" depends="init" if="isaDir">
<echo message="foo is is a directory" />
</target>
<target name="all" depends="exists,isFile,isDir" />
</project>Execute this build file three times. Once with an empty directory, once after creating directory foo, and once after removing directory and creating file foo.
$ ls
build.xml
$ ant
Buildfile: build.xml
init:
exists:
isFile:
isDir:
all:
BUILD SUCCESSFUL
Total time: 3 seconds
Ant/Setting 41 $ mkdir foo
mkdir foo
Ant/Setting 42 $ ant
ant
Buildfile: build.xml
init:
exists:
[echo] foo exists
isFile:
isDir:
[echo] foo is is a directory
all:
BUILD SUCCESSFUL
Total time: 3 seconds
$ rmdir foo
$ touch foo
$ ant
Buildfile: build.xml
init:
exists:
[echo] foo exists
isFile:
[echo] foo is is a file
isDir:
all:
BUILD SUCCESSFUL
Total time: 3 secondsModifying Property Files with Ant
I was recently testing a Web Service that expected a unique key be passed with each invocation. I could have used the date/time or I could have persisted a counter in a database. Instead, I used the optional ant propertyfile task to persist a counter in a property file.
Here is a simple ant build file demonstrating how to increment a counter in a Java properties file. The propertyfile file attribute specifies the name of the property file and each nested entry element specifies how the value of a property file key should be modified. In this case, the value of the key "count" is treated as an int and the value 1 is added upon each invocation.
Here is a simple ant build file demonstrating how to increment a counter in a Java properties file. The propertyfile file attribute specifies the name of the property file and each nested entry element specifies how the value of a property file key should be modified. In this case, the value of the key "count" is treated as an int and the value 1 is added upon each invocation.
<project default="run">
<target name="run">
<propertyfile file="test.properties">
<entry key="count" type="int" operation="+" value="1" />
</propertyfile>
</target>
</project>Here is the build file in operation.
1 $ cat test.properties
count=100
2 $ ant run
Buildfile: build.xml
run:
[propertyfile] Updating property file: /Projects/Learn/Ant/propertyfile/test.properties
BUILD SUCCESSFUL
Total time: 0 seconds
3 $ cat test.properties
#Sun Oct 01 14:11:06 EDT 2006
count=101The propertyfile entry element also allows us to specify a default value.
<project default="run">
<target name="run">
<propertyfile file="test.properties">
<entry key="count" default="10" type="int" operation="+" value="1" />
</propertyfile>
</target>
</project>
1 $ rm test.properties
2 $ ant run
Buildfile: build.xml
run:
[propertyfile] Creating new property file: /Projects/Learn/Ant/propertyfile/test.properties
BUILD SUCCESSFUL
Total time: 0 seconds
3 $ cat test.properties
#Sun Oct 01 14:30:18 EDT 2006
count=11
4 $ ant run
Buildfile: build.xml
run:
[propertyfile] Updating property file: /Projects/Learn/Ant/propertyfile/test.properties
BUILD SUCCESSFUL
Total time: 0 seconds
5 $ cat test.properties
#Sun Oct 01 14:37:12 EDT 2006
count=12And finally, another test to show that any keys not explicitly specified are left alone.
1 $ cat test.properties
#Sun Oct 01 14:37:12 EDT 2006
count=12
more=another value
andmore=yet another value
2 $ ant run
Buildfile: build.xml
run:
[propertyfile] Updating property file: /Projects/Learn/Ant/propertyfile/test.properties
BUILD SUCCESSFUL
Total time: 0 seconds
3 $ cat test.properties
#Sun Oct 01 14:47:09 EDT 2006
andmore=yet another value
more=another value
count=13Ant Hello World Revisited
We'll continue our Ant tutorials with "Hello World," only now we'll execute a Java class. Start by creating a simple hello class as shown below.
public class hello {
public static void main( String[] args )
{
System.out.println( "Hello World" );
}
}From the command line, compiling, jarring and executing this class is as simple as:
$ javac hello.java
$ jar -cvf hello.jar hello.class
added manifest
adding: hello.class(in = 415) (out= 285)deflated 31%)
$ java -classpath hello.jar hello
Hello WorldNow let's write an Ant build file to compile. Naturally, you should start by reading the fine manual description of the javac task. We'll just use the srcdir attribute to compile all java files in the directory tree rooted at srcdir.
<project default="compile">
<target name="compile">
<javac srcdir="." />
</target>
</project>Executing ant from the command line:
$ rm *.class
$ ant -f hello.xml compile
Buildfile: hello.xml
compile:
[javac] Compiling 1 source file
BUILD SUCCESSFUL
Total time: 4 secondsNow, let's add a target to create the jar (added lines are shown in bold). The destfile attribute is required and both the basedir and the includes attributes are necessary in this case. The jar file will only contain the manifest file without the basedir attribute and the includes attribute is required to exclude all the non-class files (in particular, the jar file cannot contain itself). The regular expression used in the includes attribute will match all class files in the directory tree rooted at basedir.
<project default="compile">
<target name="compile">
<javac srcdir="." />
</target>
<target name="jar" depends="compile">
<jar destfile="hello.jar"
basedir="."
includes="**/*.class"
/>
</target>
</project>Executing ant from the command line:
$ rm *.jar
$ ant -f hello.xml jar
Buildfile: hello.xml
compile:
jar:
[jar] Building jar: /Tutorial/Ant/Jar/hello.jar
BUILD SUCCESSFUL
Total time: 2 seconds
$ jar -tvf hello.jar
jar -tvf hello.jar
0 Wed Jan 22 17:06:32 EST 2003 META-INF/
55 Wed Jan 22 17:06:32 EST 2003 META-INF/MANIFEST.MF
335 Wed Jan 22 16:36:16 EST 2003 hello.classFinally, let's add a target to execute our class (added lines in bold). We insure that the jar is always built first by having the execution target run depend upon the jar target. In this example, both the classname and the classpath attributes of the java task are used to completely specify the class to execute — eliminating a dependence upon the CLASSPATH environment variable. And we request a new JVM by setting fork="true"; providing the class full access to the java runtime and preventing a call to System.exit() from terminating the ant process.
<project default="compile">
<target name="compile">
<javac srcdir="." />
</target>
<target name="jar" depends="compile">
<jar destfile="hello.jar"
basedir="."
includes="**/*.class"
/>
</target>
<target name="run" depends="jar">
<java classname="hello"
classpath="hello.jar"
fork="true"
/>
</target>
</project>And execute:
$ant -f hello.xml run
Buildfile: hello.xml
compile:
jar:
run:
[java] Hello World
BUILD SUCCESSFUL
Total time: 2 secondsAnt Hello World
We'll continue our Ant tutorials with "Hello World," only now we'll execute a Java class. Start by creating a simple hello class as shown below.
public class hello {
public static void main( String[] args )
{
System.out.println( "Hello World" );
}
}From the command line, compiling, jarring and executing this class is as simple as:
$ javac hello.java
$ jar -cvf hello.jar hello.class
added manifest
adding: hello.class(in = 415) (out= 285)deflated 31%)
$ java -classpath hello.jar hello
Hello WorldNow let's write an Ant build file to compile. Naturally, you should start by reading the fine manual description of the javac task. We'll just use the srcdir attribute to compile all java files in the directory tree rooted at srcdir.
<project default="compile">
<target name="compile">
<javac srcdir="." />
</target>
</project>Executing ant from the command line:
$ rm *.class
$ ant -f hello.xml compile
Buildfile: hello.xml
compile:
[javac] Compiling 1 source file
BUILD SUCCESSFUL
Total time: 4 secondsNow, let's add a target to create the jar (added lines are shown in bold). The destfile attribute is required and both the basedir and the includes attributes are necessary in this case. The jar file will only contain the manifest file without the basedir attribute and the includes attribute is required to exclude all the non-class files (in particular, the jar file cannot contain itself). The regular expression used in the includes attribute will match all class files in the directory tree rooted at basedir.
<project default="compile">
<target name="compile">
<javac srcdir="." />
</target>
<target name="jar" depends="compile">
<jar destfile="hello.jar"
basedir="."
includes="**/*.class"
/>
</target>
</project>Executing ant from the command line:
$ rm *.jar
$ ant -f hello.xml jar
Buildfile: hello.xml
compile:
jar:
[jar] Building jar: /Tutorial/Ant/Jar/hello.jar
BUILD SUCCESSFUL
Total time: 2 seconds
$ jar -tvf hello.jar
jar -tvf hello.jar
0 Wed Jan 22 17:06:32 EST 2003 META-INF/
55 Wed Jan 22 17:06:32 EST 2003 META-INF/MANIFEST.MF
335 Wed Jan 22 16:36:16 EST 2003 hello.classFinally, let's add a target to execute our class (added lines in bold). We insure that the jar is always built first by having the execution target run depend upon the jar target. In this example, both the classname and the classpath attributes of the java task are used to completely specify the class to execute — eliminating a dependence upon the CLASSPATH environment variable. And we request a new JVM by setting fork="true"; providing the class full access to the java runtime and preventing a call to System.exit() from terminating the ant process.
<project default="compile">
<target name="compile">
<javac srcdir="." />
</target>
<target name="jar" depends="compile">
<jar destfile="hello.jar"
basedir="."
includes="**/*.class"
/>
</target>
<target name="run" depends="jar">
<java classname="hello"
classpath="hello.jar"
fork="true"
/>
</target>
</project>And execute:
$ant -f hello.xml run
Buildfile: hello.xml
compile:
jar:
run:
[java] Hello World
BUILD SUCCESSFUL
Total time: 2 seconds
Subscribe to:
Posts (Atom)