Clean way to combine multiple jars? Preferably using Ant

JavaAntJar

Java Problem Overview


I have runtime dependencies on some external jars that I would like to "rejar" into a single jar. These external dependencies are stored in an external_jars directory, and I'd like to be able to not have to list them all (i.e., not to need to change my build scripts if my dependencies change). Any thoughts?

Google gave me a good answer on how to do this - if you don't mind listing out each jar as a dependency:

http://markmail.org/message/zijbwm46maxzzoo5

Roughly, I want something along the lines of the following, which would combine all jars in the lib directory into out.jar (with some sane overwrite rules).

jar -combine -out out.jar -in lib/*.jar

Java Solutions


Solution 1 - Java

Vladimir's answer is a correct one, but I feel that what he suggests implies repacking all jars in a one big out.jar, which is then feeded to Ant Jar task as a single <zipfileset> or something like that. This two-step approach is unnecessary. I'm not sure whether this is connected with Ant version, but I have Ant 1.7.1, and its <jar> task understands <zipgroupfileset>, which allows to feed all contents of third party jars' directly.

<jar destfile="MyApplication.jar">
  <zipgroupfileset dir="lib" includes="*.jar" /> 
  <!-- other options -->
  <manifest>
    <attribute name="Main-Class" value="Main.MainClass" />
  </manifest>
</jar>

Solution 2 - Java

Just use zipgroupfileset with the Ant Zip task

<zip destfile="out.jar">
    <zipgroupfileset dir="lib" includes="*.jar"/>
</zip>

This will flatten all included jar libraries' content.

Solution 3 - Java

You could check out jarjar:

http://code.google.com/p/jarjar/

Solution 4 - Java

Try extracting your JAR's to a marshalling directory first:

<target name="combine-jars">
    <mkdir dir="${marshall.dir}"/>
    <unzip dest="${marshall.dir}">
        <fileset dir="${external.jar.dir}">
            <include name="**/*.jar"/>
        </fileset>
    </unzip>
    <jar destfile="${combined.jar}" basedir="${marshall.dir"}>
    <delete dir="${marshall.dir}"/>
</target>

Where ${marshall.dir} is a temporary directory, ${external.jar.dir} is where you keep the JAR's, and ${combined.jar} is the target JAR.

Solution 5 - Java

If using maven, why wouldn't you ? :) Just use the maven-shade-plugin, works like a charm !

  <project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>1.5</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <mainClass>com.YOUR_COMPANY.YOUR_MAIN_CLASS</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

Solution 6 - Java

This is my solution:

<target name="-post-jar">
    <echo>Packaging ${application.title} into a single JAR</echo>

    <jar destfile="${basedir}${file.separator}${dist.dir}${file.separator}_${ant.project.name}_.jar">
        <zipgroupfileset dir="${basedir}${file.separator}${dist.dir}" includes="${ant.project.name}.jar"/>
        <zipgroupfileset dir="${basedir}${file.separator}${dist.dir}${file.separator}lib" includes="*.jar"/>
        <manifest>
            <attribute name="Main-Class" value="${main.class}"/>
        </manifest>
    </jar>
</target>

Solution 7 - Java

The question is well answered. I wanted mention one tool I find useful - One-Jar. One-Jar handles resources more cleanly (by keeping all of them). This more useful if the code needs to process MANIFEST files.

Sample XML copied from website..

<import file="one-jar-ant-task.xml"/>   
 
    <target name="hello" depends="init">
        <!-- Build lib.jar -->   
        <javac destdir="${classes.dir}/lib">
            <src path="${lib.dir}" />
        </javac>
        <jar destfile="${build.dir}/lib.jar" >
            <fileset dir="${classes.dir}/lib"/>
        </jar>   
        <!-- Build classes for main.jar -->   
        <javac destdir="${classes.dir}/src">
            <src path="${src.dir}" />
            <classpath path="${build.dir}/lib.jar"/>   
        </javac>
        <!-- Construct the One-JAR file -->   
        <one-jar destfile="hello.jar" manifest="hello.mf">
            <main>
                <!-- Construct main.jar from classes and source code -->
                <fileset dir="${classes.dir}/src"/>
            </main>
            <lib>
                <fileset file="${build.dir}/lib.jar" />
            </lib>
        </one-jar>
        <echo>
          Now you can run the Hello One-JAR example using 
          $ java -jar hello.jar
        </echo>   
           
    </target>

Solution 8 - Java

If you are building with ant (I am using ant from eclipse), you can just add the extra jar files by saying to ant to add them... Not necessarily the best method if you have a project maintained by multiple people but it works for one person project and is easy.

for example my target that was building the .jar file was:

<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
    <manifest>
        <attribute name="Author" value="ntg"/>
        ................................
        <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
    </manifest>
</jar>

I just added one line to make it:

<jar ....">
	<zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
    <manifest>
        ................................
    </manifest>
</jar>

where

<property name="external-lib-dir" value="C:\...\eclipseWorkspace\Filter\external\...\lib" />

was the dir with the external jars. And that's it... You can add multiple zipgroupfileset tags as well.

Solution 9 - Java

Have you considered using Maven or some other system which manages your dependencies automatically? Then you would not need to specify where each library is located, what their names are, and what transitive dependencies your direct dependencies have. You would just state in one place what the dependency and its version are, and the system would take care of downloading the libraries, configuring the classpath and building the project.

Solution 10 - Java

Well, I am not so much in to programming - but something simpler worked for me...if the question meant - combining jar files in to one. Ofcourse, this is manual, dirty solution. I just untarred all the tars...and then..created a new tar file, by adding all the directories formed by untarring- in to the new tar file. it worked.

Solution 11 - Java

Maven or other build tools can't "manage" the resolution of multiple versions of class files. In fact, Maven causes these problems in the first place, through transitive inclusion of all downstream jar files that are not explicitly required by a project.

Suppose somewhere in the transitive closure of a project (all libraries and modules required by the project, and all it's dependent projects, recursively) there are two versions of a class file. How could Maven possibly know which one is the 'correct' one? which one was intended by the programmer?

It can't because this information was lost when explicit dependencies were thrown away in favor of transitive ones (to save XML typing).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJacobView Question on Stackoverflow
Solution 1 - JavanightingaleView Answer on Stackoverflow
Solution 2 - JavaVladimirView Answer on Stackoverflow
Solution 3 - JavaStaaleView Answer on Stackoverflow
Solution 4 - JavaDavid GrantView Answer on Stackoverflow
Solution 5 - JavaMozart BrocchiniView Answer on Stackoverflow
Solution 6 - JavaMattView Answer on Stackoverflow
Solution 7 - JavaJayanView Answer on Stackoverflow
Solution 8 - JavantgView Answer on Stackoverflow
Solution 9 - JavaEsko LuontolaView Answer on Stackoverflow
Solution 10 - JavaSowmyaView Answer on Stackoverflow
Solution 11 - Javahans andersonView Answer on Stackoverflow