Component Build for Ant

Maven is a very good, powerful way to build a Java product, that can handle a product divided into projects. Unfortunately, not all products are able to use Maven, for whatever reason (company policies, difficult learning curve, etc.). Maven also has a few requirements that don't always fit the natural shape of a product. Ant, on the other hand, is a simpler, more widely accepted way to build a Java product. It works at a lower level than Maven, and so often requires more work to achieve the same results.

When a codebase becomes larger, it becomes more and more painful to build the whole product. It would be helpful to have a "component" build, in Ant, that allows the developers to work on one part of the codebase while allowing the rest of the codebase to be nearly ignored. Once the source has been partitioned into the appropriate chunks, how do we get Ant to build the right way?

Goals

A component build should:

Directory Structure

One directory structure that supports some of the goals is:

The rest of this discussion assumes such a directory structure. While there are other perfectly good directory structures, this one is assumed so that the following arguments don't have to digress into handling all the other possibilities. It should be easy enough to modify the component build ideas to work with other directory structures (and if it isn't, that's probably a defect in the directory structure).

Build Scripts

There are two types of build script in this design: Top-level build, and component build. The top-level build gets its own "component" directory (branch/topbuild), and each component gets its own eponymous component directory.

Also, note that the examples here are simplified build scripts. The output is simple .jar files, instead of .war or .ear files, directories of Java classes, combined java and native code, or whatever else may be needed. The marker file and check file approach to preventing unnecessary build steps has no impact on, and is not affected by, the type of build output. Neither are they affected if the build is enhanced with library dependency checking, other types of tests, style checking, static code analysis, continuous integration, etc.

Top-Level Build Scripts

The master build file, the one that should be invoked by default when the user types "ant" at the command line. Thus, it should be included as branch/topbuild/build.xml.

build.xml
<?xml version="1.0"?> <!--=================================================================== Master build file for the MyProj project. ====================================================================--> <project name="MyProj" default="build" basedir="."> <!-- set up properties --> <property environment="env"/> <property name="component.name" value="MyProject"/> <property name="myproj.root.dir" value="${basedir}"/> <import file="${myproj.root.dir}/header.xml"/> <!-- Build control --> <property name="start.file" value="${build.dir}/start.marker"/> <!-- list of the subprojects, roughly in dependency order --> <filelist id="component.buildfiles" dir="."> <!-- built first --> <file name="ComponentAlef/build.xml"/> <file name="ComponentBet/build.xml"/> <file name="ComponentGimel/build.xml"/> <file name="ComponentDalet/build.xml"/> <!-- built last --> </filelist> <!--=================================================================== Builds all distributable output whether it needs it or not, and places the output into a single directory. ====================================================================--> <target name="force" description="-> build all components" depends="force-dependencies,publish"/> <!--=================================================================== Builds all distributable output that is not up to date, and places the output into a single directory. ====================================================================--> <target name="build" description="-> build all outdated components" depends="build-dependencies,publish"/> <!--=================================================================== Creates and verifies (including unit tests) all distributable output, building everything whether or not it needs it. ====================================================================--> <target name="force-dependencies"> <mkdir dir="${build.dir}"/> <touch file="${start.file}"/> <subant target="local" inheritAll="false"> <property name="label" value="${label}"/> <property name="myproj.release" value="${myproj.release}"/> <property name="checkstyle.header" value="bin/CheckStyle/standard.header"/> <property name="build.start.marker" location="${start.file}"/> <filelist refid="component.buildfiles"/> </subant> </target> <!--=================================================================== Creates and verifies (including unit tests) all distributable output, building only if the dependency is out of date. ====================================================================--> <target name="build-dependencies"> <mkdir dir="${build.dir}"/> <touch file="${start.file}"/> <subant target="build-if-needed" inheritAll="false"> <property name="label" value="${label}"/> <property name="myproj.release" value="${myproj.release}"/> <property name="checkstyle.header" value="bin/CheckStyle/standard.header"/> <property name="build.start.marker" location="${start.file}"/> <filelist refid="component.buildfiles"/> </subant> </target> <!--=================================================================== Removes all distributable and intermediate output ====================================================================--> <target name="clean" description="-> cleans up all components"> <delete failOnError="false" includeEmptyDirs="true"> <fileset dir="${build.dir}"/> <fileset dir="${dist.dir}"/> </delete> <subant target="clean" inheritAll="false"> <property name="label" value="${label}"/> <filelist refid="component.buildfiles"/> </subant> </target> </project>

Anyone who tries to use this build script, or very attentive readers, may have noticed that it imports another file, header.xml. It is found in the same directory as the master build file, as branch/topbuild/build.xml.

header.xml
<?xml version="1.0"?> <!--=================================================================== This is a generic build file, meant to be included in a component's build file. The following code block is recommended right after the <project> tag: <property environment="env"/> <property name="component.name" value="My Component"/> <property name="myproj.root.dir" value="${basedir}/../.."/> <import file="${myproj.root.dir}/header.xml"/> The second and third lines above may need to be modified for the actual name and path of the component. ====================================================================--> <project name="header"> <!-- Guard against missing properties --> <fail unless="component.name" message="Must define component.name before import."/> <fail unless="myproj.root.dir" message="Must define myproj.root.dir before import."/> <!-- Properties for the build --> <property file="${myproj.root.dir}/build.properties"/> <!-- Standard location: source files --> <property name="src.dir" value="${basedir}/src"/> <property name="java.src.dir" value="${src.dir}/java"/> <!-- Standard location: test source files --> <property name="test.dir" value="${basedir}/test"/> <property name="java.test.dir" value="${test.dir}/java"/> <!-- Standard location: libraries, tools, binaries --> <property name="lib.dir" value="${basedir}/lib"/> <property name="tools.dir" value="${basedir}/tools"/> <!-- Standard location: intermediate output files --> <property name="build.dir" value="${basedir}/build"/> <property name="testbuild.dir" value="${basedir}/testbuild"/> <property name="java.build.dir" value="${build.dir}/java"/> <property name="lib.build.dir" value="${build.dir}/lib"/> <property name="javadoc.build.dir" value="${build.dir}/javadoc"/> <!-- Standard location: log files --> <property name="log.dir" value="${build.dir}/logs"/> <property name="test.results.dir" value="${log.dir}/jtest"/> <!-- Standard location: distributable output files --> <property name="dist.dir" value="${basedir}/dist"/> <!-- Default label, used when not part of an official build. --> <tstamp> <format property="build.time" pattern="d MMMM yyyy, HH:mm"/> </tstamp> <condition property="env.HOSTNAME" value="${env.COMPUTERNAME}"> <isset property="env.COMPUTERNAME"/> </condition> <property name="env.HOSTNAME" value="unknown computer"/> <property name="label" value="Unlabelled ${component.name} build of ${build.time} on ${env.HOSTNAME}."/> <property name="myproj.release" value="${DSTAMP}${TSTAMP}"/> <!-- OS properties --> <condition property="os.unix"> <os family="unix"/> </condition> <condition property="os.linux"> <os family="unix" name="linux"/> </condition> <condition property="os.solaris"> <os family="unix" name="sunos"/> </condition> <condition property="os.windows"> <os family="windows"/> </condition> <!-- standard top-level locations --> <property name="global.bin.dir" value="${myproj.root.dir}/bin"/> <property name="global.lib.dir" value="${myproj.root.dir}/lib"/> <property name="global.tools.dir" value="${myproj.root.dir}/tools"/> <path id="ant.lib.path"> <fileset includes="*.jar" dir="${global.bin.dir}/apache-ant-1.8.0/lib"/> </path> <!--=================================================================== Build the project, without checking or building any dependencies. Note: This target assumes that the including buildfile has a target named <build>, which will create the entire component. ====================================================================--> <target name="local" description="-> creates component, without dependencies"> <echo level="info" message=" *** Ignoring dependencies"/> <property name="suppress.component.check" value="true"/> <antcall target="build"/> </target> </project>

The topbuild directory also contains some Ant scripts that are used in the component builds. This one defines the standard classpaths used for building the source, and for building and running unit tests. It is found at branch/topbuild/classpath.xml.

classpath.xml
<?xml version="1.0"?> <!--======================================================================= Generic build file for handling Java classpath elements. This is a generic build file, meant to be included in a component's build file. It depends on some properties defined in header.xml, which must be imported first. The following code block is recommended right after the <project> tag: <property environment="env"/> <property name="component.name" value="Common Library"/> <property name="myproj.root.dir" value="${basedir}/../.."/> <import file="${myproj.root.dir}/header.xml"/> This generic build file also depends on a few other elements. The property 'jdk.lib.classpath' must be defined, and will be interpreted as the classpath used for the Java SDK class definitions. A file set, with id of 'external.classpath.files', must be created, containing the set of files to be added to the classpath. This code block is recommended a little further on: <property name="jdk.lib.classpath" value="..."/> <patternset id="external.classpath.files"> The dependency files belonging on the classpath, e.g. <include name="OtherComponent/dist/*.jar"/> </patternset> <import file="${myproj.root.dir}/classpath.xml"/> Note that the file nodependency.xml defines classpaths for a component that has no external classpath files. ========================================================================--> <project name="classpath"> <!-- Guard against missing properties --> <fail unless="java.build.dir" message="Must define java.build.dir before import."/> <fail unless="lib.dir" message="Must define lib.dir before import."/> <fail unless="jdk.lib.classpath" message="Must define jdk.lib.classpath before import."/> <!-- set to false explicitly; won't override if already set --> <property name="debug" value="false"/> <property name="deprecation" value="false"/> <property name="failfast" value="false"/> <property name="optimize" value="false"/> <!-- standard path elements. These paths depend on a <patternset id="external.classpath.files"> to define the output files of interest to the project, relative to the MyProj root directory. This fileset must refer to all foreign output files that this project depends on. --> <!-- the classpath used to compile source and tests --> <path id="standard.compile.classpath"> <pathelement location="${java.build.dir}"/> <fileset dir="${myproj.root.dir}"> <patternset refid="external.classpath.files"/> </fileset> <pathelement path="${jdk.lib.classpath}"/> <fileset dir="${lib.dir}"> <include name="**/*.jar"/> </fileset> </path> <!-- the classpath used to run tests --> <path id="standard.test.classpath"> <fileset dir="${test.build.dir}"> <include name="*.jar"/> </fileset> <fileset dir="${java.build.dir}"> <include name="*.jar"/> </fileset> <fileset dir="${myproj.root.dir}"> <patternset refid="external.classpath.files"/> </fileset> <pathelement path="${jdk.lib.classpath}"/> <fileset dir="${lib.dir}"> <include name="*.jar"/> </fileset> </path> </project>

The dependency.xml script is included in any component build script whose component depends on one or more other components. It establishes several targets that are needed to allow the component to act as a dependency and to use others as dependencies. It is found at branch/topbuild/dependency.xml.

dependency.xml
<?xml version="1.0"?> <!--=================================================================== Generic build file for handling dependencies. This file establishes mechanisms by which this component may depend on other components, and mechanisms by which other components may depend on this one. For a component which has no dependencies, use the file nodependency.xml in the same directory as this one. This is a generic build file, meant to be included in a component's build file. It depends on some properties defined in header.xml, which must be imported first. The following code block is recommended right after the <project> tag: <property environment="env"/> <property name="component.name" value="ComponentAlef"/> <property name="myproj.root.dir" value="${basedir}/../.."/> <import file="${myproj.root.dir}/header.xml"/> This generic build file also depends on a few other elements. The property 'jdk.lib.classpath' must be defined, and will be interpreted as the classpath used for the Java SDK class definitions. A directory set, with id of 'dependency.build.dirs', must be created, containing a directory for each dependency; the directory must be the one containing that dependency's build.xml file. This code block is recommended a little further on: <property name="jdk.lib.classpath" value="..."/> <dirset id="dependency.build.dirs" dir="${myproj.root.dir}"> The directory containing build.xml for each dependency, e.g. <include name="ComponentBet"/> </dirset> <fileset id="dependency.classpath.files" dir="${myproj.root.dir}"> The dependency files belonging on the classpath, e.g. <include name="ComponentBet/dist/*.jar"/> </fileset> <fileset id="dependency.output.files" dir="${myproj.root.dir}"> <patternset refid="dependency.classpath.files"/> Plus any other files needed, but not belonging on the classpath </fileset> <import file="${myproj.root.dir}/dependency.xml"/> ====================================================================--> <project name="dependency"> <!-- Guard against missing properties --> <fail unless="build.dir" message="Must define build.dir before import."/> <fail unless="component.name" message="Must define component.name before import."/> <!-- Build control --> <property name="marker.file" value="${build.dir}/build.marker"/> <property name="check.file" value="${build.dir}/check.marker"/> <property name="start.file" value="${build.dir}/start.marker"/> <!--=================================================================== Build ourselves if anything we depend on has changed. This target is used by the dependency mechanisms to build only what has changed, and must exist in any component that may be depended on. ====================================================================--> <target name="build-if-needed" if="${component.name}.outofdate" depends="check-uptodate"> <echo message="${component.name} is out of date; building."/> <antcall target="build"/> </target> <!--=================================================================== Builds all dependencies, if needed. This target depends on a <dirset id="dependence.build.dirs"> to define the build directories. This dirset must contain one directory for each dependency; the directory must be the one containing the dependency's build.xml. ====================================================================--> <target name="dependencies" unless="suppress.component.check" depends="mark-start"> <!-- Call build-if-needed for each dependency --> <subant inheritAll="false" target="build-if-needed"> <property name="build.start.marker" value="${build.start.marker}"/> <property name="ESM.branch" value="${ESM.branch}"/> <dirset refid="dependency.build.dirs"/> </subant> </target> <!--=================================================================== Check if the build for this is up to date. This target depends on a <fileset id="dependency.output.files"> to define the output files of interest to the project. This fileset must refer to all foreign output files that this project depends on. ====================================================================--> <target name="check-uptodate" depends="dependencies,alreadychecked" unless="${component.name}.alreadychecked"> <!-- Fail if our properties are set already. --> <fail if="dependencies.uptodate" message="dependencies.uptodate already set!"/> <fail if="${component.name}.src.uptodate" message="${component.name}.src.uptodate already set!"/> <fail if="${component.name}.bld.uptodate" message="${component.name}.bld.uptodate already set!"/> <fail if="${component.name}.outofdate" message="${component.name}.outofdate already set!"/> <echo message="Checking ${component.name} for changed source."/> <!-- Check our dependencies against our marker. --> <uptodate property="dependencies.uptodate" targetfile="${marker.file}"> <srcfiles refid="dependency.output.files"/> </uptodate> <!-- Check our source against our marker. --> <uptodate property="${component.name}.src.uptodate" targetfile="${marker.file}"> <srcfiles dir="${src.dir}"/> </uptodate> <uptodate property="${component.name}.bld.uptodate" targetfile="${marker.file}"> <srcfiles dir="${basedir}" includes="*.xml"/> </uptodate> <!-- Set the out-of-date property. --> <condition property="${component.name}.outofdate"> <not> <and> <isset property="dependencies.uptodate"/> <isset property="${component.name}.src.uptodate"/> <isset property="${component.name}.bld.uptodate"/> </and> </not> </condition> <mkdir dir="${build.dir}"/> <touch file="${check.file}"/> </target> <!--=================================================================== Check if we've already checked that we're up to date in this build. ====================================================================--> <target name="alreadychecked"> <!-- Fail if required info not available. --> <fail unless="build.start.marker" message="Property build.start.marker not set!"/> <uptodate property="${component.name}.alreadychecked" targetfile="${check.file}" srcfile="${build.start.marker}"/> </target> <!--=================================================================== If this is the build that was invoked, create a start marker file and pass the location around. ====================================================================--> <target name="mark-start" unless="build.start.marker"> <mkdir dir="${build.dir}"/> <touch file="${start.file}"/> <property name="build.start.marker" location="${start.file}"/> </target> </project>

The nodependency.xml script is included in any component build script whose component does not depend on any other components. It establishes several targets that are needed to allow the component to act as a dependency. It is found at branch/topbuild/nodependency.xml.

nodependency.xml
<?xml version="1.0"?> <!--=================================================================== Generic build file for handling dependencies, for a component that has no dependencies. This file handles the mechanisms by which another component may depend on this one. For a component which has dependencies, use the file dependency.xml in the same directory as this one. This is a generic build file, meant to be included in a component's build file. It depends on some properties defined in header.xml, which must be imported first. The following code block is recommended right after the <project> tag: <property environment="env"/> <property name="component.name" value="ComponentGimel"/> <property name="myproj.root.dir" value="${basedir}/../.."/> <import file="${myproj.root.dir}/header.xml"/> <property name="jdk.lib.classpath" value="....."/> <import file="${myproj.root.dir}/nodependency.xml"/> ====================================================================--> <project name="nodependency"> <!-- Guard against missing properties --> <fail unless="build.dir" message="Must define build.dir before import."/> <fail unless="component.name" message="Must define component.name before import."/> <fail unless="jdk.lib.classpath" message="Must define jdk.lib.classpath before import."/> <!-- Build control --> <property name="marker.file" value="${build.dir}/build.marker"/> <property name="check.file" value="${build.dir}/check.marker"/> <!-- standard path elements. These properties depends on a <fileset id="dependency.output.files"> to define the output files of interest to the project. This fileset must refer to all foreign output files that this project depends on. --> <!-- the classpath used to compile source and tests --> <path id="standard.compile.classpath"> <pathelement location="${java.build.dir}"/> <pathelement path="${jdk.lib.classpath}"/> <fileset dir="${lib.dir}"> <include name="*.jar"/> </fileset> </path> <!-- the classpath used to run tests --> <path id="standard.test.classpath"> <fileset dir="${test.build.dir}"> <include name="*.jar"/> </fileset> <fileset dir="${java.build.dir}"> <include name="*.jar"/> </fileset> <pathelement path="${jdk.lib.classpath}"/> <fileset dir="${lib.dir}"> <include name="*.jar"/> </fileset> </path> <!--=================================================================== Build ourselves if anything we depend on has changed. This target is used by the dependency mechanisms to build only what has changed, and must exist in any component that may be depended on. ====================================================================--> <target name="build-if-needed" if="${component.name}.outofdate" depends="check-uptodate"> <echo message="${component.name} is out of date; building."/> <antcall target="build" inheritAll="false"/> </target> <!--=================================================================== Check if the build for this is up to date. This target depends on a <fileset id="dependency.output.files"> to define the output files of interest to the project. This fileset must refer to all foreign output files that this project depends on. ====================================================================--> <target name="check-uptodate" depends="alreadychecked" unless="${component.name}.alreadychecked"> <!-- Fail if our properties are set already. --> <fail if="dependencies.uptodate" message="dependencies.uptodate already set!"/> <fail if="${component.name}.src.uptodate" message="${component.name}.src.uptodate already set!"/> <fail if="${component.name}.bld.uptodate" message="${component.name}.bld.uptodate already set!"/> <fail if="${component.name}.outofdate" message="${component.name}.outofdate already set!"/> <echo message="Checking ${component.name} for changed source."/> <!-- Check our source against our marker. --> <uptodate property="${component.name}.src.uptodate" targetfile="${marker.file}"> <srcfiles dir="${src.dir}"/> </uptodate> <uptodate property="${component.name}.bld.uptodate" targetfile="${marker.file}"> <srcfiles dir="${basedir}" includes="*.xml"/> </uptodate> <!-- Set the out-of-date property. --> <condition property="${component.name}.outofdate"> <not> <and> <isset property="${component.name}.src.uptodate"/> <isset property="${component.name}.bld.uptodate"/> </and> </not> </condition> <mkdir dir="${build.dir}"/> <touch file="${check.file}"/> </target> <!--=================================================================== Check to see if we've already checked that we're up to date in this build. This target is used to prevent spending time examining the source timestamps more than once; without it, the build could spend several minutes re-checking modules. ====================================================================--> <target name="alreadychecked"> <!-- Fail if required info not available. --> <fail unless="build.start.marker" message="Property build.start.marker not set!"/> <uptodate property="${component.name}.alreadychecked" targetfile="${check.file}" srcfile="${build.start.marker}"/> </target> </project>

Component Build Scripts

Components with no dependencies have a build file that does not look for dependencies. The build script is placed at the top of the component's tree, named branch/component/build.xml.

build.xml (no dependencies)
<?xml version="1.0"?> <!--=================================================================== This is the build file for component Gimel. ====================================================================--> <project name="componentgimel" default="build" basedir="."> <!-- Set up standard header --> <property environment="env"/> <property name="component.name" value="ComponentGimel"/> <property name="myproj.root.dir" location="${basedir}/.."/> <import file="${myproj.root.dir}/header.xml"/> <!-- Dependency properties (none for this component) --> <import file="${myproj.root.dir}/nodependency.xml"/> <!--=================================================================== Creates and verifies (including unit tests) all distributable output ====================================================================--> <target name="build" depends="test,publish" description="-> creates component"> <mkdir dir="${build.dir}"/> <touch file="${marker.file}"/> <touch file="${check.file}"/> </target> <!--=================================================================== Places all distributable output in the correct directory. ====================================================================--> <target name="publish" depends="package"> <mkdir dir="${dist.dir}"/> <!-- Copy the files the project built --> <copy file="${java.build.dir}/${component.name}.jar" todir="${dist.dir}" preservelastmodified="true"/> </target> <!--=================================================================== Runs unit tests and otherwise verifies distributable output. To run a single unit test, add "-Dclassname=<FQCN>" ====================================================================--> <target name="test" depends="package" description="-> runs unit tests"> <fail if="junit.error" message="junit.error must not be set before tests."/> <mkdir dir="${test.results.dir}"/> <!-- Copy the data files to the necessary location --> <copy todir="${testbuild.dir}"> <fileset dir="${test.dir}/data"/> </copy> <!-- run the tests --> <junit fork="yes" dir="${test.build.dir}" failureproperty="junit.error" haltonfailure="${failfast}"> <jvmarg line="-server"/> <classpath refid="test.classpath"/> <formatter type="plain" usefile="false"/> <formatter type="xml" usefile="true"/> <test name="${classname}" todir="${test.results.dir}" if="classname"/> <batchtest todir="${test.results.dir}" unless="classname"> <fileset dir="${test.build.dir}"> <exclude name="**/*$*.class"/> </fileset> </batchtest> </junit> <fail if="junit.error" message="Java unit test failure."/> </target> <!--=================================================================== Creates final form output from intermediate output, without verifying ====================================================================--> <target name="package" depends="compile"> <jar destfile="${java.build.dir}/${component.name}.jar"> <manifest> <attribute name="Build" value="${label}"/> </manifest> <fileset dir="${java.build.dir}" includes="**/*.class/> </jar> <jar destfile="${testbuild.dir}/${component.name}-test.jar"> <manifest> <attribute name="Build" value="${label}"/> </manifest> <fileset dir="${testbuild.dir}" includes="**/*.class"/> </jar> </target> <!--=================================================================== Transforms source and test files into intermediate output ====================================================================--> <target name="compile"/> <mkdir dir="${java.build.dir}"/> <javac srcdir="${java.src.dir}" destdir="${java.build.dir}" source="${java.source.version}" target="${java.target.version}" debug="${debug}" deprecation="${deprecation}" optimize="${optimize}"> <include name="**/*.java"/> <classpath refid="compile.classpath"/> </javac> <mkdir dir="${testbuild.dir}"/> <javac srcdir="${java.test.dir}" destdir="${testbuild.dir}" source="${java.source.version}" target="${java.target.version}" debug="true" deprecation="${deprecation}" optimize="${optimize}"> <include name="**/*.java"/> <classpath refid="compile.classpath"/> </javac> </target> <!--=================================================================== Removes all distributable and intermediate output ====================================================================--> <target name="clean" description="-> cleans up the component"> <delete failOnError="false" includeEmptyDirs="true"> <fileset dir="${build.dir}"/> <fileset dir="${dist.dir}"/> </delete> <delete file="${marker.file}" quiet="true"/> <delete file="${check.file}" quiet="true"/> </target> </project>

Components with dependencies have a build file that can trigger a build for the dependencies as well. The build script is placed at the top of the component's tree, named branch/component/build.xml.

build.xml (dependencies)
<?xml version="1.0"?> <!--=================================================================== This is the build file for component Bet. ====================================================================--> <project name="componentbet" default="build" basedir="."> <!-- set up standard header --> <property environment="env"/> <property name="component.name" value="ComponentBet"/> <property name="myproj.root.dir" location="${basedir}/.."/> <import file="${myproj.root.dir}/header.xml"/> <!-- Compilation properties --> <patternset id="external.classpath.files"> <include name="ComponentGimel/dist/*.jar"/> <include name="ComponentDalet/*.jar"/> </patternset> <import file="${myproj.root.dir}/classpath.xml"/> <!-- Dependency properties --> <dirset id="dependency.build.dirs" dir="${myproj.root.dir}"> <include name="ComponentGimel"/> <include name="ComponentDalet"/> </dirset> <fileset id="dependency.output.files" dir="${myproj.root.dir}"> <patternset refid="external.classpath.files"/> </fileset> <import file="${myproj.root.dir}/dependency.xml"/> <!--=================================================================== Creates and verifies (including unit tests) all distributable output ====================================================================--> <target name="build" depends="test" description="-> creates component, including dependencies"> <copy file="${java.build.dir}/${component.name}.jar" todir="${dist.dir}" preservelastmodified="true"/> <mkdir dir="${build.dir}"/> <touch file="${marker.file}"/> <touch file="${check.file}"/> </target> <!--=================================================================== Runs unit tests and otherwise verifies distributable output. To run a single unit test, add "-Dclassname=<FQCN>". ====================================================================--> <target name="test" depends="package" description="-> runs unit tests"> <mkdir dir="${test.results.dir}"/> <junit printsummary="yes" fork="yes" dir="${test.build.dir}"> <classpath refid="test.classpath"/> <formatter type="plain" usefile="false"/> <formatter type="xml" usefile="true"/> <test name="${classname}" if="classname" todir="${test.results.dir}"/> <batchtest todir="${test.results.dir}" unless="classname"> <fileset dir="${test.build.dir}" excludes="**/*$*.class"/> </batchtest> </junit> </target> <!--=================================================================== Creates distributable from intermediate output, without verifying ====================================================================--> <target name="package" depends="compile"> <jar destfile="${java.build.dir}/${component.name}.jar"> <manifest> <attribute name="Build" value="${label}"/> </manifest> <fileset dir="${java.build.dir}"> <include name="**/*.class"/> </fileset> </jar> <jar destfile="${testbuild.dir}/${component.name}-test.jar"> <manifest> <attribute name="Build" value="${label}"/> </manifest> <fileset dir="${test.build.dir}"> <include name="**/*.class"/> </fileset> </jar> </target> <!--=================================================================== Transforms source and test files into intermediate output ====================================================================--> <target name="compile" description="-> compiles the source and tests" depends="dependencies"> <mkdir dir="${java.build.dir}"/> <javac srcdir="${java.src.dir}" destdir="${java.build.dir}" source="${java.source.version}" target="${java.target.version}" debug="${debug}" deprecation="${deprecation}" optimize="${optimize}"> <include name="**/*.java"/> <classpath refid="compile.classpath"/> </javac> <mkdir dir="${testbuild.dir}"/> <javac srcdir="${java.test.dir}" destdir="${test.build.dir}" source="${java.source.version}" target="${java.target.version}" debug="true" deprecation="${deprecation}" optimize="${optimize}"> <include name="**/*.java"/> <classpath refid="compile.classpath"/> </javac> </target> <!--=================================================================== Removes all distributable and intermediate output ====================================================================--> <target name="clean" description="-> cleans up the component"> <delete failOnError="false" includeEmptyDirs="true"> <fileset dir="${build.dir}"/> <fileset dir="${dist.dir}"/> </delete> </target> </project>