can't make jar with dependencies extracted in it via ant - ant

I want to build my project using ant but I have a small problem. My problem is that I need the output jar have all my .class and all my jar dependencies extracted not zipped.
<project name="ivy example" default="compress" xmlns:ivy="antlib:org.apache.ivy.ant">
<target name="resolve" description="Resolve and retrieve with ivy">
<ivy:resolve />
<ivy:cachepath pathid="compile.path" />
</target>
<target name="compile" depends="resolve" description="compilation">
<mkdir dir="build/classes" />
<javac srcdir="src" destdir="build/classes">
<classpath refid="compile.path" />
</javac>
</target>
<target name="compress" depends="compile">
<jar destfile="output/engine.jar" filesonly="true" update="true">
<fileset dir="build/classes" />
<fileset dir="lib"/>
<manifest>
<attribute name="Created-By" value="vireton"/>
<attribute name="Main-Class" value="HelloIvy"/>
</manifest>
</jar>
<echo>Building .jar file completed successfully!</echo>
</target>
</project>
That code generates my engine.jar with the output classes + dependencies.jar.
I want it to generate my classes and the dependencies extracted.
Can anyone help?

i did it by extracting the dependencies to a tmp directory then make a jar of both directories (src & tmp)
<target name="compress" depends="compile">
<delete file="output/engine.jar" />
<mkdir dir="tmp" />
<unzip dest="tmp">
<fileset dir="lib">
<include name="*.jar" />
</fileset>
</unzip>
<delete dir="tmp/META-INF" />
<jar destfile="output/engine.jar" update="true">
<fileset dir="build/classes" />
<fileset dir="tmp"/>
<manifest>
<attribute name="Created-By" value="vireton"/>
<attribute name="Main-Class" value="HelloIvy"/>
</manifest>
</jar>
</target>
and i found an even better way to do it using
<target name="compress" depends="compile">
<delete file="output/engine.jar" />
<jar destfile="output/engine.jar" update="true">
<zipgroupfileset dir="lib" includes="*.jar"/>
<zipfileset dir="build/classes" />
</jar>
</target>

Extract all the jars of the lib folder to some temporary folder (using the unjar task), then create the engine.jar by adding this temporary folder as a fileset.
Just like you would do it by hand.

You have your dependency jar files available as a path thanks to
<ivy:cachepath pathid="compile.path" />
so the simplest way to bundle them all into your final jar would be (assuming Ant 1.8 or later)
<jar destfile="output/engine.jar" filesonly="true" update="true">
<fileset dir="build/classes" />
<archives>
<zips>
<path refid="compile.path"/>
</zips>
</archives>
<manifest>
<attribute name="Created-By" value="vireton"/>
<attribute name="Main-Class" value="HelloIvy"/>
</manifest>
</jar>
This will read the jars directly from your ivy cache, you don't need to retrieve them into a local lib directory first.

Related

Ant Build issue : Additonal folder getting created

need your help at the earliest and I have been breaking my head over this for a while.
Below is my Ant Script.
<property name="build.dir" value="${basedir}/build"/>
<property name="lib.dir" value="${basedir}/lib"/>
<property name="src.dir" value="${basedir}/allocator"/>
<property name="classes.dir" value="${basedir}/classes"/>
<property name="jar.dir" value="${basedir}/jar"/>
<property name="main-class" value="allocator.Allocator"/>
<path id="classpath_ref">
<pathelement path="${basedir}/"/>
<fileset dir="${lib.dir}" includes="*.jar"/>
</path>
<target name="clean">
<delete dir="${classes.dir}" />
<delete dir="${build.dir}" />
<delete dir="${jar.dir}" />
</target>
<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath_ref" includeantruntime="false">
<src path="${basedir}/supportlibraries"/>
<src path="${basedir}/com/xyz/framework"/>
</javac>
<copy todir="${build.dir}"><fileset dir="${src.dir}" excludes="**/*.java"/>
</copy>
</target>
<target name="jar" depends="compile">
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${build.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
</manifest>
</jar>
</target>
<target name="run" depends="jar">
<java classname="${main-class}" fork="true" >
<arg line="username"/>
<arg line="password"/>
<classpath>
<path refid="classpath_ref"/>
<pathelement location="${jar.dir}/${ant.project.name}.jar"/>
</classpath>
</java>
</target>
<target name="main" depends="clean,run"/>
</project>
when i try to run the ant build from eclipse it is creating an additional folder allocator, supportlibraries and com/xyz/framework inside the class folder. why is it creating additional folder? because of the additional folders when the run target executes its not able to locate the allocator class. Please let me know if i am missing anything
Remove the <copy> from the "compile" <target>.
The <jar> in the "jar" target should be:
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
</manifest>
</jar>
In the above snippet, basedir is ${classes.dir} instead of ${build.dir}.
I don't see a need for build.dir in your script. All references to it can likely be removed.

Ant With Internal Dependencies

I have a a jar right now that uses external dependencies. I'm trying to create a jar that packages all the external dependencies inside, and will just give me one jar. I saw this question asked multiple times, but I still can't figure it out. I'm using Ant, and copied some of the examples I saw on here. I'm using zipgroupfileset to reference the external(now internal) jars. As soon as I added the zipgroupfileset I got a runtime error that said my Runner class could not be found.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- WARNING: Eclipse auto-generated file.
Any modifications will be overwritten.
To include a user specific buildfile here, simply create one in the same
directory with the processing instruction <?eclipse.ant.import?>
as the first entry and export the buildfile again. -->
<project basedir="." default="build" name="ExcelDemo">
<property environment="env"/>
<property name="ECLIPSE_HOME" value="../../../../Program Files (x86)/eclipse"/>
<property name="debuglevel" value="source,lines,vars"/>
<property name="target" value="1.6"/>
<property name="source" value="1.6"/>
<property name="external-lib-dir" value="lib\poi-3.9" />
<property name="external-lib-dir2" value="lib\poi-3.9\lib" />
<property name="external-lib-dir3" value="lib\poi-3.9\ooxml-lib" />
<path id="ExcelDemo.classpath">
<pathelement location="bin"/>
</path>
<target name="init">
<mkdir dir="bin"/>
<copy includeemptydirs="false" todir="bin">
<fileset dir="src" excludes="**/*.launch, **/*.java"/>
</copy>
</target>
<target name="clean">
<delete dir="bin"/>
</target>
<target depends="clean" name="cleanall"/>
<target depends="build-subprojects,build-project" name="build"/>
<target name="build-subprojects"/>
<target depends="init" name="build-project">
<echo message="${ant.project.name}: ${ant.file}"/>
<javac debug="true" debuglevel="${debuglevel}" destdir="bin" source="${source}" target="${target}">
<src path="src"/>
<classpath refid="ExcelDemo.classpath"/>
</javac>
</target>
<target description="Build all projects which reference this project. Useful to propagate changes." name="build-refprojects">
<ant antfile="${ExcelSensitize.location}/build.xml" inheritAll="false" target="clean"/>
<ant antfile="${ExcelSensitize.location}/build.xml" inheritAll="false" target="build">
<propertyset>
<propertyref name="build.compiler"/>
</propertyset>
</ant>
</target>
<target description="copy Eclipse compiler jars to ant lib directory" name="init-eclipse-compiler">
<copy todir="${ant.library.dir}">
<fileset dir="${ECLIPSE_HOME}/plugins" includes="org.eclipse.jdt.core_*.jar"/>
</copy>
<unzip dest="${ant.library.dir}">
<patternset includes="jdtCompilerAdapter.jar"/>
<fileset dir="${ECLIPSE_HOME}/plugins" includes="org.eclipse.jdt.core_*.jar"/>
</unzip>
</target>
<target description="compile project with Eclipse compiler" name="build-eclipse-compiler">
<property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter"/>
<antcall target="build"/>
</target>
<target name="RunnerClass">
<java classname="runner.RunnerClass" failonerror="true" fork="yes">
<classpath refid="ExcelDemo.classpath"/>
</java>
</target>
<target name="jar" description="Create a jar for this project">
<manifestclasspath property="lib.list" jarfile="Test.jar">
<classpath refid="ExcelDemo.classpath" />
</manifestclasspath>
<jar jarfile="Test.jar" includes="*.class" basedir="bin">
<zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
<zipgroupfileset dir="${external-lib-dir2}" includes="*.jar"/>
<zipgroupfileset dir="${external-lib-dir3}" includes="*.jar"/>
<manifest>
<attribute name="Class-Path" value="${lib.list}" />
<attribute name="Main-Class" value="runner.RunnerClass" />
</manifest>
</jar>
</target>
</project>
To make things simpler:
Create a separate sources jar for compilation. Then, have a separate compiled jar without the sources.
Don't include the third party jars. Instead, use Ivy with Ant. Ant will automatically download the required jars. In fact, I've see sources that just include the ivy.jar, so Ivy will automatically be configured when you unjar the sources. You type in ant, and everything just builds.
As an alternative, you can look at Maven which is how many projects are now packaged. In fact, if your jar is an open source project, you can probably host it on the OSS Maven repository. This way, no one even needs to manually download your compiled jar. If they want it, they configure their Maven project to do it for them.
i think the problem is that you use basedir="bin" in the your jar task. then path of your zipgroupfileset convert to bin/${external-lib-dir}

How to get hold on the flattenmapper result in ant?

Please, find below a few targets from my ant file:
<fileset id="test-dep-jars" dir="o:/java">
<include name="junit-4.10.jar"/>
<include name="easymock-3.1\easymock-3.1.jar"/>
<include name="easymockclassextension-3.1\easymockclassextension-3.1.jar"/>
</fileset>
<target name="copy-test-deps">
<mkdir dir="${deploy.dir}"/>
<copy todir="${deploy.dir}">
<fileset refid="test-dep-jars"/>
<flattenmapper/>
</copy>
</target>
<target name="jar" depends="copy-test-deps">
<jar destfile="${deploy.dir}/test-${ant.project.name}.jar" basedir="${test.classes.dir}"
includes="**/*.class" filesetmanifest="skip">
<manifest>
<attribute name="Class-Path"
value="${ant.project.name}.jar junit-4.10.jar easymock-3.1.jar easymockclassextension-3.1.jar"/>
</manifest>
</jar>
</target>
My problem is that I have to state the test dependency jars twice - once when defining the test-dep-jars fileset and the second time when specifying the Class-Path manifest attribute of the produced jar.
If I only could get hold on the flattenmapper result, then I would be able to use it in the Class-Path as is.
How can I get hold on the flattenmapper result?
Thanks.
If you want to use flattenmapper you can use following...
<pathconvert property="mf.classpath" pathsep=" ">
<path refid="build.class.path" />
<flattenmapper />
</pathconvert>
<manifest>
<attribute name="Class-Path"
value="${ant.project.name}.jar ${mf.classpath}"/>
</manifest>
I would recommend using the manifestclasspath task instead:
<manifestclasspath property="jar.classpath" jarfile="${jar.file}">
<classpath>
<fileset dir="${deploy.dir}" includes="*.jar"/>
</classpath>
</manifestclasspath>
<jar destfile="${jar.file}" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="${jar.main.class}" />
<attribute name="Class-Path" value="${jar.classpath}" />
</manifest>
</jar>
It will generate the correct classpath property definition and even works with relative paths (for example if you were to place the dependent jars in sub-directory).

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

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