69

I'm using Ant to build some Java projects.
In some, I've got a lib/ directory, which contains external dependencies, in the form on JAR files.

During the build, I create a bundled jar, that contains the project's code, alongside the dependencies, by adding to the bundle jar file a zipfileset for each of the jars in the lib/ directory.

The problem is, that every time I add a jar, or change names, I need to remember to update the build.xml file, as I couldn't find a way for adding those zipfilesets in an automatic manner that will include all jars in a certain pattern (e.g. lib/*.jar).

Is there a better way for doing this?

I've considered writing my own Ant Task for this, or using Groovy's ant API to do this programmatically, but was wondering if there's a way for doing this using "vanilla" ant.

rajadilipkolli
  • 3,475
  • 2
  • 26
  • 49
abyx
  • 69,862
  • 18
  • 95
  • 117

4 Answers4

58

In my target, I have something like this:

<jar destfile="${store.dir}/temp_final.jar" filesetmanifest="skip">
    <zipgroupfileset dir="dist" includes="*.jar"/>
    <zipgroupfileset dir="dist/lib" includes="*.jar" excludes=""/>

    <manifest>
        <attribute name="Main-Class" value="${main.class}"/>
        <attribute name="Class-Path" value="${mf.classpath}"/>
    </manifest>
</jar>

And here is how I build my classpath:

<path id="build.classpath">
    <fileset dir="${basedir}/">
        <include name="${lib.dir}/*.jar"/>
    </fileset>
</path>

<pathconvert property="mf.classpath" pathsep=" ">
    <path refid="build.classpath"/>
    <mapper>
        <chainedmapper>
            <flattenmapper/>
            <globmapper from="*.jar" to="lib/*.jar"/>
        </chainedmapper>
    </mapper>
</pathconvert>

mf.classpath is used from the package target posted above. This part I copied from somewhere else, so I'm not all that familiar with it.

Quick edit. Javac needs to know about those jars too.

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

<target name="compile">
    <mkdir dir="${build.dir}"/>
    <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="jars" debug="on"/>
</target>
jonescb
  • 22,013
  • 7
  • 46
  • 42
  • 1
    Here is a similar method to jonescb's one containing the final `build.xml` (the big picture) which is missing in this answer: http://www.mkyong.com/ant/ant-create-a-fat-jar-file/ – Stephan Jul 01 '15 at 14:28
35

Use a zipgroupfileset. For example:

<target name="jar">
    <jar destfile="foo.jar" basedir="${dir.classes}">
        <zipgroupfileset dir="lib" includes="*.jar"/>
    </jar>
</target>

The zipgroupfileset is documented with the Zip task.

martin clayton
  • 76,436
  • 32
  • 213
  • 198
Jason Day
  • 8,809
  • 1
  • 41
  • 46
  • As jonescb answered, that really is the solution. Never saw it, as it's hidden in the Zip task – abyx Dec 06 '09 at 17:29
  • The big problem with this is if the jar files have extra files, typically in the MANIFEST directory. For example, many of the spring framework jars contain spring.handlers, spring.schemas and spring.tooling files that.. well, I haven't figured how to handle these.. merging? – ticktock May 09 '14 at 22:22
  • 1
    @GeorgeBaxter Been looking for the same thing and ended up doing [this](http://stackoverflow.com/a/24083318/2208271). Let me know if you find a better solution. – Sithsu Jun 06 '14 at 13:44
  • Thanks @Sithsu. I also rolled my own solution [here](http://stackoverflow.com/questions/23574979/creating-an-uber-jar-with-spring-dependencies?noredirect=1#comment37141427_23574979), though someone didn't like my question for some reason... – ticktock Jun 16 '14 at 21:01
  • Simplest and perfect. This is better solution than the selected answer. Thanks. – José Cabo May 21 '15 at 15:15
10

For those of you using NetBeans here is how you can create JAR archive with libraries bundled using zipgroupfileset:

<target name="-post-jar">

    <property name="store.jar.name" value="MyJarName"/>

    <property name="store.dir" value="dist"/>
    <property name="store.jar" value="${store.dir}/${store.jar.name}.jar"/>

    <echo message="Packaging ${application.title} into a single JAR at ${store.jar}"/>

    <jar destfile="${store.dir}/temp_final.jar" filesetmanifest="skip">
        <zipgroupfileset dir="dist" includes="*.jar"/>
        <zipgroupfileset dir="dist/lib" includes="*.jar"/>

        <manifest>
            <attribute name="Main-Class" value="${main.class}"/>
        </manifest>
    </jar>

    <zip destfile="${store.jar}">
        <zipfileset src="${store.dir}/temp_final.jar"
        excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
    </zip>

    <delete file="${store.dir}/temp_final.jar"/>
    <delete dir="${store.dir}/lib"/>
    <delete file="${store.dir}/README.TXT"/>
</target>

I've added this target definition to the end of build.xml file. Target name is -post-jar.

0

Here's an simple example of an ant target that will create a jar (named test.jar) that includes all jar files under the lib directory. Maybe this will solve your problem?

<target name="jar">
    <jar destfile="test.jar">
        <fileset dir="lib/" includes="**/*.jar" />
    </jar>
</target>
Upgradingdave
  • 12,916
  • 10
  • 62
  • 72