Ant

If, after reading Java post, someone is curious if it’s possible to use makefile to compile Java applications – the answer would be yes.
But there are better ways to do it. One of them is Ant.
You can install it on Linux (sudo apt-get install ant or sudo yum install ant) and get it for Windows (don’t forget update system PATH).

Similar to make utility, Ant reads configuration file and executes some commands. Default file name for Ant is build.xml.
Let’s create one for our Java post:

<project>

    <target name="clean">
        <delete dir="out"/>
        <delete dir="classes"/>
    </target>

    <target name="compile">
        <mkdir dir="classes"/>
        <javac srcdir="src" destdir="classes"/>
    </target>

    <target name="jar">
        <mkdir dir="out"/>
        <jar destfile="out/HelloWorld.jar" basedir="classes">
            <manifest>
                <attribute name="Main-Class" value="ak.HelloWorld"/>
            </manifest>
        </jar>
    </target>

    <target name="run">
        <java jar="out/HelloWorld.jar" fork="true"/>
    </target>

</project>

As you can guess we have four targets and for each there are set of commands. Difference with make is that in make you specify command directly, and has a build-in commands to perform curtain tasks: delete dir, mkdir, javac, jar etc.

First target – clean; action – delete two folders.
Second target – compile; actions: create directory and run Java compiler
Third target – jar; action – create directory and create jar archive
Fourth target – run; action – executes our application

to execute you would run

ant clean
ant build
ant jar 
ant run

Now let’s do some improvements.
– Similar to makefile we don’t want to type all the directories throughout the file, we can create properties for that (in my example I named each property with the name as a directory).
– We can specify default target.
– We can specify dependency for targets

<project name="HelloWorld" basedir="." default="jar">

    <property name="src"    value="src"/>
    <property name="classes" 	value="classes"/>
    <property name="out"     	value="out"/>
    <property name="main-class" value="ak.HelloWorld"/>

    <target name="clean">
        <delete dir="${out}"/>
        <delete dir="${classes}"/>
    </target>

    <target name="compile">
        <mkdir dir="${classes}"/>
        <javac srcdir="${src}" destdir="${classes}"/>
    </target>

    <target name="jar" depends="compile">
        <mkdir dir="${out}"/>
        <jar destfile="${out}/${ant.project.name}.jar" basedir="${classes}">
            <manifest>
                <attribute name="Main-Class" value="${main-class}"/>
            </manifest>
        </jar>
    </target>

    <target name="run" depends="jar">
        <java jar="${out}/${ant.project.name}.jar" fork="true"/>
    </target>

    <target name="clean-build" depends="clean,jar"/>

</project>

But Ant is much more powerful. It can run unit test for you and produce a report.
First some preparations. First you will need to get a JUnit libraries. Here you can download two jar files. You may need to update ant as well: sudo yum install ant-junit or sudo apt-get install ant-optional
So, let’s create a folder java2, inside it create two more folders: src and lib. Copy junit jars to lib folder. And in src we would create two Java files. One is slightly modified HelloWorld.java (and we put it in subfolder ak, by it’s package name):

package ak;

public class HelloWorld
{
	public HelloWorld() {}

	public static void main(String args[])
	{
		System.out.println("Hello Java");
	}

	public int abs(int x)
	{
		return x > 0 ? x : x;
	}
}

and we then we create unit test – HelloWorldTest.java (right in src folder):

import org.junit.*;
import ak.*;

public class HelloWorldTest extends junit.framework.TestCase {

    public void testOkay() {
	HelloWorld hw = new HelloWorld();
	int nResult = hw.abs(1);
	assertEquals(1, nResult);
    }
    
    public void testWillFail() {
	HelloWorld hw = new HelloWorld();
	int nResult = hw.abs(-1);
	assertEquals(1, nResult);
    }
    
}

and now is our final version of build.xml

<project name="HelloWorld" basedir="." default="jar">

    <property name="src"    value="src"/>
    <property name="classes" 	value="classes"/>
    <property name="out"     	value="out"/>
    <property name="main-class" value="ak.HelloWorld"/>
    <property name="lib"        value="lib"/>
    <property name="report"        value="unit_report"/>

    <path id="classpath">
	    <fileset dir="${lib}" includes="**/*.jar"/>
    </path>

    <path id="application" location="${jar}/${ant.project.name}.jar"/>

    <target name="clean">
        <delete dir="${out}"/>
        <delete dir="${classes}"/>
	<delete dir="${report}"/>
    </target>

    <target name="compile">
        <mkdir dir="${classes}"/>
        <javac srcdir="${src}" destdir="${classes}" classpathref="classpath"/>
    </target>

    <target name="jar" depends="compile">
        <mkdir dir="${out}"/>
        <jar destfile="${out}/${ant.project.name}.jar" basedir="${classes}">
            <manifest>
                <attribute name="Main-Class" value="${main-class}"/>
            </manifest>
        </jar>
    </target>

    <target name="run" depends="jar">
        <java jar="${out}/${ant.project.name}.jar" fork="true"/>
    </target>

    <target name="clean-build" depends="clean,jar"/>

    <target name="junit">
	<mkdir dir="${report}"/>
        <junit printsummary="yes">
            <classpath>
                <path refid="classpath"/>
		<path refid="application"/>
		<path location="${out}/${ant.project.name}.jar"/>
            </classpath>

	    <formatter type="xml"/>
       
	    <batchtest fork="yes" todir="${report}">
                <fileset dir="${src}" includes="*Test.java"/>
            </batchtest>
        </junit>
    </target>

    <target name="junitreport">
        <junitreport todir="${report}">
            <fileset dir="${report}" includes="TEST-*.xml"/>
            <report todir="${report}"/>
        </junitreport>
    </target>

    </project>

to test it we
– compile

ant clean-build

– do a test run

ant run

– run unit test

ant junit

– create a report

ant junitreport

now you can open unit_report/index.html file and get unit test report
if you do everything write you should get a report with one passed test and one failed test.

If you want to run JUnit manually you can do it like this:

java -cp out/HelloWorld.jar:lib/junit-4.12.jar:lib/hamcrest-core-1.3.jar org.junit.runner.JUnitCore HelloWorldTest

The final version of the code is available at GitHub.

Leave a Reply