Clean way to combine multiple jars? Preferably using Ant

旧街凉风 提交于 2019-12-17 02:26:55

问题


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

回答1:


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.




回答2:


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>



回答3:


You could check out jarjar:

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




回答4:


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.




回答5:


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>



回答6:


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>



回答7:


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.




回答8:


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>



回答9:


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.




回答10:


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.




回答11:


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).



来源:https://stackoverflow.com/questions/515428/clean-way-to-combine-multiple-jars-preferably-using-ant

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!