ant multiple source directories with copied resources - ant

Consider minimal build.xml fragment which builds jar from sources and includes all non-java resources:
<property name="src.dir" value="src" />
<target name="build">
<javac destdir="bin">
<src path="${src.dir}" />
</javac>
<copy includeemptydirs="false" todir="bin">
<fileset dir="${src.dir}">
<exclude name="**/*.java" />
</fileset>
</copy>
<jar destfile="dist/foo.jar" basedir="bin"/>
</target>
Now imagine that I need to support a list of source directories:
<property name="src.dirs" value="src;src-gen" />
How can i modify above script to make it happen ?
javac will happily take list of directories but for copy I need to transform string into list of filesets with exclusions or find some other way.

Normally, you simply list them all together:
<javac destdir="bin">
<src path="${src.dir}"/>
<src path="${src2.dir}"/>
<src path="${src3.dir}"/>
</javac>
You can try the <sourcepath/> attribute. I've never used it, but I believe you can use it to define a path of various source files, and use that:
<path id="source.path">
<pathelement path="${src.dir}"/>
<pathelement path="${src2.dir}"/>
<pathelement path="${src4.dir}"/>
</path>
<javac destdir="bin">
srcpathref="source.path"/>
The first will work, but not 100% sure about the second.

I'm not sure of a way to do it with built-in Ant tasks but you could use an ant-contrib <for> task
<path id="src.path">
<pathelement location="src" />
<pathelement location="src-gen" />
</path>
<target name="build">
<javac destdir="bin">
<src refid="src.path" />
</javac>
<for param="dir">
<path refid="src.path" />
<sequential>
<copy includeemptydirs="false" todir="bin">
<fileset dir="#{dir}">
<exclude name="**/*.java" />
</fileset>
</copy>
</sequential>
</for>
<jar destfile="dist/foo.jar" basedir="bin"/>
</target>

The simple solution is to just specify multiple filesets, in the same manner as the javac task supports multiple "src" attributes:
<target name="build" depends="init" description="Create the package">
<javac destdir="${classes.dir}" includeantruntime="false">
<src path="src/main1/java"/>
<src path="src/main2/java"/>
</javac>
<copy todir="${classes.dir}" includeemptydirs="false">
<fileset dir="src/main1" excludes="**/*.java"/>
<fileset dir="src/main2" excludes="**/*.java"/>
<flattenmapper/>
</copy>
</target>
This of course assumes that the number of source code locations is fixed, which is not unreasonable to expect.
If you want to drive this using a list property you must resort to embedding a script within your build to process this list (I can't endorse ant-contrib):
<project name="demo" default="build">
<property name="src.dirs" value="src/main1,src/main2"/>
<property name="build.dir" location="build"/>
<property name="classes.dir" location="${build.dir}/classes"/>
<target name="bootstrap">
<mkdir dir="${user.home}/.ant/lib"/>
<get dest="${user.home}/.ant/lib/groovy-all.jar" src="http://search.maven.org/remotecontent?filepath=org/codehaus/groovy/groovy-all/2.1.6/groovy-all-2.1.6.jar"/>
</target>
<target name="init">
<mkdir dir="${classes.dir}"/>
</target>
<target name="build" depends="init" description="Create the package">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy"/>
<groovy>
def srcDirs = properties["src.dirs"].split(",")
ant.javac(destdir:properties["classes.dir"], includeantruntime:false) {
srcDirs.each {
src(path:"${it}/java")
}
}
ant.copy(todir:properties["classes.dir"], includeemptydirs:false) {
srcDirs.each {
fileset(dir:it, excludes:"**/*.java")
}
flattenmapper()
}
</groovy>
</target>
<target name="clean" description="Cleanup build dirs">
<delete dir="${build.dir}"/>
</target>
</project>
Notes:
Compare the "build" targets. You'll notice that the groovy solution calls ANT in the same manner. This is why I really like groovy's integration with ANT.
Example also includes a "bootstrap" target to download the groovy jar dependency from Maven Central. You could alternatively use ivy to manage your build's dependencies.

A simple solution without ant-contrib tasks or embedded scripts:
<property name="src.dirs" value="src,src-gen" />
<path id="src.path">
<multirootfileset type="dir" basedirs="${src.dirs}"/>
</path>
<target name="build">
<javac destdir="bin">
<src refid="src.path"/>
</javac>
<copy todir="bin">
<multirootfileset type="file" basedirs="${src.dirs}">
<exclude name="**/*.java"/>
</multirootfileset>
</copy>
<jar destfile="dist/foo.jar" basedir="bin"/>
</target>
multirootfileset to the rescue! ;-) Needs Ant 1.9.4 or higher.

Related

Ant - Generate .class files even if compiler errors

How to successfully build using Ant, if the code has compilation errors?
If I have 3 .java files and 1 has a compilation error, is there anything that can make my build successful and can give me the remaining 2 .class files?
<target name="build" description="Compiles the Source code" depends="Directory.check">
<echo>Compilation Starts</echo>
<javac failonerror="false" includeantruntime="false" destdir="${build.dir}"
deprecation="false" optimize="true" executable="${exec.dir}">
<compilerarg value="-Xlint:unchecked"/>
<compilerarg value="-Xlint:deprecation"/>
<src path="${src.dir}"/>
<classpath refid="master-classpath"/>
</javac>
<echo>Compilation Ends</echo>
</target>
Iterate though src files using for from ant-contrib.
Pass the file name to javac in includes attribute with the src basedir as srcdir.
<target name="compile" depends="init" description="compile the source ">
<for param="file">
<path>
<fileset dir="${src}" includes="**/*.java" excludes="**/Sanity/*.java"/>
</path>
<sequential>
<local name="program"/>
<basename property="program" file="#{file}" suffix=".java"/>
<javac includeantruntime="false" srcdir="${src}" debug="on" includes="**/${program}.java"
excludes="**/Sanity/*.java" destdir="${build}" failonerror="false"
verbose="true">
<compilerarg value="-Xbootclasspath/p:${toString:lib.path.ref} -Xlint:deprecation -Xlint:unchecked"/>
</javac>
</sequential>
</for>
</target>

Multiple renaming using ant script

Let me explain the scenario:
D:\project\src\one.txt
D:\project\src\two.txt
D:\project\src\three.txt
D:\project\src\four.txt
The above files should be copied as :
D:\project\dst\one.xls
D:\project\dst\two.xls
D:\project\dst\three.xls
D:\project\dst\four.xls
I need to change the extension without using the mapper and move task. I need to rename as above using a for loop with fte:filecopy function inside. Is this possible ???
For anyone arriving here without the negative requirement afflicting the OP, the much simpler answer is to use a mapper.
<project default="move_files">
<target name="move_files">
<copy todir="dst">
<fileset dir="src">
<include name="*.txt"/>
</fileset>
<globmapper from="*.txt" to="*.xls"/>
</copy>
</target>
</project>
This works for me :
<?xml version="1.0"?>
<project name="so-copy-rename" default="build2">
<property name="ant-contrib-jar" value="${user.home}/.ant/lib/ant-contrib-1.0b3.jar"/>
<target name="setup" unless="ant-contrib.present">
<echo>Getting ant-contrib</echo>
<mkdir dir="${user.home}/.ant/lib"/>
<!--
Note: change this to a locally hosted maven repository manager such as nexus http://nexus.sonatype.org/
-->
<get dest="${ant-contrib-jar}"
src="http://repo1.maven.org/maven2/ant-contrib/ant-contrib/1.0b3/ant-contrib-1.0b3.jar"/>
</target>
<target name="taskdefs">
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="${ant-contrib-jar}"/>
</classpath>
</taskdef>
</target>
<target name="build" depends="taskdefs">
<property name="srcdir" value="src"/>
<property name="targetdir" value="target"/>
<property name="files" value="file1,file2,file3,file4"/>
<mkdir dir="${targetdir}"/>
<foreach list="${files}" target="copy-rename" param="srcfile" trim="true">
<param name="srcdir" value="${srcdir}" />
<param name="targetdir" value="${targetdir}" />
</foreach>
</target>
<target name="copy-rename">
<var name="src-suffix" value="txt"/>
<var name="tgt-suffix" value="xls"/>
<copy file="${srcdir}/${srcfile}.${src-suffix}" tofile="${targetdir}/${srcfile}.${tgt-suffix}" />
</target>
<target name="build2" depends="taskdefs">
<property name="srcdir" value="src"/>
<property name="targetdir" value="target"/>
<mkdir dir="${targetdir}"/>
<foreach target="copy-rename2" param="srcfile">
<path id="srcfilepath">
<fileset dir="${srcdir}" casesensitive="yes">
<include name="*.txt"/>
</fileset>
</path>
<param name="targetdir" value="${targetdir}" />
</foreach>
</target>
<target name="copy-rename2">
<var name="basefile" value="" unset="true"/>
<basename property="basefile" file="${srcfile}" suffix=".txt"/>
<var name="tgt-suffix" value="xls"/>
<copy file="${srcfile}" tofile="${targetdir}/${basefile}.${tgt-suffix}" />
</target>
</project>
Can you slice it the other way and perform the renaming inside the fte:filecopy command? Looking at the IBM documentation, you can specify tasks to be carried out at the source or destination agents either before or after the copy, using presrc, postdst etc. This task could be an Ant task that does the renaming?

Execute a jar file using Ant

I am trying to create a runnable jar file from java classes using ant. The java classes use external jars. When I execute the build.xml its showing class not found exception while running the java program. Its compiling fine.
Part of My source code:
<path id="project-libpath">
<fileset dir="${lib.dir}">
<include name="*.jar"/>
</fileset>
</path>
<path id="project-classpath">
<fileset dir="C:/xmldecode/lib">
<include name="*.jar"/>
</fileset>
</path>
<target name="compile" depends="prepare">
<javac srcdir="${src.dir}" destdir="${classes.dir}">
<classpath refid="project-classpath"/>
</javac>
</target>
<target name="jar" depends="compile">
<copy todir="${classes.dir}">
<fileset dir="C:/xmldecode/lib"/>
</copy>
<pathconvert property="mf.classpath" pathsep=";">
<path refid="project-classpath" />
<flattenmapper />
</pathconvert>
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
<attribute name="Class-Path" value="${mf.classpath}"/>
</manifest>
</jar>
</target>
<target name="run" depends="jar">
<java jar="${jar.dir}/${ant.project.name}.jar" fork="true">
</java>
Your problem is that the manifest classpath entries are not separated by a ";" character.
The following will work better I think:
<pathconvert property="mf.classpath" pathsep=" ">
<path refid="project-classpath" />
<flattenmapper />
</pathconvert>
Could I suggest using the new ANT task manifestclasspath ?
<manifestclasspath property="mf.classpath" jarfile="${jar.dir}/${ant.project.name}.jar">
<classpath refid="project-classpath" />
</manifestclasspath>
This powerful method will determine paths relative to the jar's location, for example if the jar's dependencies are located in a lib directory

Ant for EJB project

I am trying to make an ant script for an EJB project. I am using Jboss for this.
I am new to both EJB and Ant and am having problems in getting the beans to compile from ant. It gives me number of errors of the kind
package javax.persistence does not exist
#MappedSuperclass - Cannot find symbol
I created it as an eclipse project initially, and added the jboss runtime through eclipse. Do I need to copy all the jars in a lib folder and include them in the classpath for the beans to compile or is there a better way to do this?
Try setting classpath in Ant appropriately. It ignores any system CLASSPATH or Eclipse setting that you have.
Here's an Ant build.xml that you can start with:
<?xml version="1.0" encoding="UTF-8"?>
<project name="spring-finance" basedir="." default="package">
<property name="version" value="1.6"/>
<property name="haltonfailure" value="no"/>
<property name="out" value="out"/>
<property name="production.src" value="src/main/java"/>
<property name="production.lib" value="src/main/webapp/WEB-INF/lib"/>
<property name="production.resources" value="src/main/resources"/>
<property name="production.classes" value="${out}/production/${ant.project.name}"/>
<property name="test.src" value="src/test/java"/>
<property name="test.lib" value="src/test/lib"/>
<property name="test.resources" value="src/test/resources"/>
<property name="test.classes" value="${out}/test/${ant.project.name}"/>
<property name="exploded" value="out/exploded/${ant.project.name}"/>
<property name="exploded.classes" value="${exploded}/WEB-INF/classes"/>
<property name="exploded.lib" value="${exploded}/WEB-INF/lib"/>
<property name="reports.out" value="${out}/reports"/>
<property name="junit.out" value="${reports.out}/junit"/>
<property name="web.src" value="src/main/webapp"/>
<property name="web.lib" value="${web.src}/WEB-INF/lib"/>
<property name="web.classes" value="${web.src}/WEB-INF/classes"/>
<path id="production.class.path">
<pathelement location="${production.classes}"/>
<pathelement location="${production.resources}"/>
<fileset dir="${production.lib}">
<include name="**/*.jar"/>
<exclude name="**/junit*.jar"/>
<exclude name="**/*test*.jar"/>
</fileset>
</path>
<path id="test.class.path">
<path refid="production.class.path"/>
<pathelement location="${test.classes}"/>
<pathelement location="${test.resources}"/>
<fileset dir="${test.lib}">
<include name="**/junit*.jar"/>
<include name="**/*test*.jar"/>
</fileset>
</path>
<available file="${out}" property="outputExists"/>
<target name="clean" description="remove all generated artifacts" if="outputExists">
<delete dir="${out}" includeEmptyDirs="true"/>
</target>
<target name="create" description="create the output directories" unless="outputExists">
<mkdir dir="${production.classes}"/>
<mkdir dir="${test.classes}"/>
<mkdir dir="${junit.out}"/>
<mkdir dir="${exploded.classes}"/>
<mkdir dir="${exploded.lib}"/>
</target>
<target name="compile" description="compile all .java source files" depends="create">
<!-- Debug output
<property name="production.class.path" refid="production.class.path"/>
<echo message="${production.class.path}"/>
-->
<javac srcdir="src" destdir="${out}/production/${ant.project.name}" debug="on" source="${version}">
<classpath refid="production.class.path"/>
<include name="**/*.java"/>
<exclude name="**/*Test.java"/>
</javac>
<javac srcdir="${test.src}" destdir="${out}/test/${ant.project.name}" debug="on" source="${version}">
<classpath refid="test.class.path"/>
<include name="**/*Test.java"/>
</javac>
</target>
<target name="test" description="run all unit tests" depends="compile">
<!-- Debug output
<property name="test.class.path" refid="test.class.path"/>
<echo message="${test.class.path}"/>
-->
<junit printsummary="yes" haltonfailure="${haltonfailure}">
<classpath refid="test.class.path"/>
<formatter type="xml"/>
<batchtest fork="yes" todir="${junit.out}">
<fileset dir="${test.src}">
<include name="**/*Test.java"/>
</fileset>
</batchtest>
</junit>
<junitreport todir="${junit.out}">
<fileset dir="${junit.out}">
<include name="TEST-*.xml"/>
</fileset>
<report todir="${junit.out}" format="frames"/>
</junitreport>
</target>
<target name="exploded" description="create exploded deployment" depends="test">
<copy todir="${exploded}">
<fileset dir="${web.src}"/>
</copy>
<copy todir="${exploded}/WEB-INF">
<fileset dir="${web.src}/WEB-INF"/>
</copy>
<copy todir="${exploded.classes}">
<fileset dir="${production.classes}"/>
</copy>
<copy todir="${exploded.lib}">
<fileset dir="${production.lib}"/>
</copy>
</target>
<target name="jar" description="create jar file" depends="test">
<jar destfile="${out}/${ant.project.name}.jar" basedir="${production.classes}" includes="**/*.class"/>
</target>
<target name="war" description="create war file" depends="exploded">
<war basedir="${exploded}" webxml="${exploded}/WEB-INF/web.xml" destfile="${out}/${ant.project.name}.war"/>
</target>
<target name="package" description="create package for deployment" depends="test">
<antcall target="war"/>
</target>
</project>

Generate manifest class-path from <classpath> in Ant

In the build file below, the jar target refers to the jar.class.path property for the manifest class-path. The compile target refers to project.class.path
There is redundancy here, because jar.class.path and project.class.path are very similar. They must be both updated when libraries are added, which can be a pain if the list of libraries gets very long. Is there a better way? Any solution must be cross-platform and always use relative paths.
Edit:
It should generate the JAR classpath from a fileset and not the other way around, so I can use wildcards to e.g. include all JAR files in a directory.
<?xml version="1.0"?>
<project name="Higgins" default="jar" basedir=".">
<property name="jar.class.path" value="lib/forms-1.2.0.jar lib/BrowserLauncher.jar"/>
<path id="project.class.path">
<pathelement location="build"/>
<fileset dir="lib">
<include name="forms-1.2.0.jar"/>
<include name="BrowserLauncher.jar"/>
</fileset>
</path>
<target name="prepare">
<mkdir dir="build"/>
</target>
<target name="compile" depends="prepare" description="Compile core sources">
<javac srcdir="src"
includes="**"
destdir="build"
debug="true"
source="1.5">
<classpath refid="project.class.path"/>
</javac>
</target>
<target name="jar" depends="compile" description="Generates executable jar file">
<jar jarfile="higgins.jar">
<manifest>
<attribute name="Main-Class" value="nl.helixsoft.higgins.Main"/>
<attribute name="Class-Path" value="${jar.class.path}"/>
</manifest>
<fileset dir="build" includes="**/*.class"/>
<fileset dir="src" includes="**/*.properties"/>
</jar>
</target>
</project>
<path id="build.classpath">
<fileset dir="${basedir}">
<include name="lib/*.jar"/>
</fileset>
</path>
<pathconvert property="manifest.classpath" pathsep=" ">
<path refid="build.classpath"/>
<mapper>
<chainedmapper>
<flattenmapper/>
<globmapper from="*.jar" to="lib/*.jar"/>
</chainedmapper>
</mapper>
</pathconvert>
<target depends="compile" name="buildjar">
<jar jarfile="${basedir}/${test.jar}">
<fileset dir="${build}" />
<manifest>
<attribute name="Main-Class" value="com.mycompany.TestMain"/>
<attribute name="Class-Path" value="${manifest.classpath}"/>
</manifest>
</jar>
</target>
For further information check out this article.
Assuming Ant 1.7 or above, you can use the manifestclasspath task.
<path id="dep.runtime">
<fileset dir="./lib">
<include name="**/*.jar" />
</fileset>
</path>
<property name="dep_cp" value="${toString:dep.runtime}" />
<target name="default">
<manifestclasspath property="manifest_cp" jarfile="myjar.jar">
<classpath refid="dep.runtime" />
</manifestclasspath>
<echo message="Build Classpath: ${dep_cp}" />
<echo message="Manifest Classpath: ${manifest_cp}" />
</target>
If you just want a common subpath shared between two (or more) paths, that is easy to do:
<path id="lib.path>
<fileset dir="lib">
<include name="forms-1.2.0.jar"/>
<include name="BrowserLauncher.jar"/>
</fileset>
</path>
<path id="project.class.path">
<pathelement location="build"/>
<path refid="lib.path"/>
</path>
<property name="jar.class.path" refid="lib.path"/>
EDIT Sorry, I misunderstood the question. Try this:
<property name="jar.class.path" value="lib/forms-1.2.0.jar lib/BrowserLauncher.jar"/>
<path id="project.class.path">
<pathelement location="build"/>
<fileset dir="." includes="${jar.class.path}"/>
</path>
You can use <pathconvert> to convert a path (which can contain a fileset) into a plain string. You'll likely need to <echo> that string to a file, use either <replace> or <replaceregexp> to chop the leading path bits, then finally use <loadfile> to load the manipulated string into the final property.
Implementation left as an exercise to the reader.

Resources