HOWTO Create MANIFEST.MF Classpath From Ant

UPDATE It seems this is at least partly outdated, have a look at manifestclasspath.

It is possible to define .jar dependencies in the MANIFEST.MF of the main .jar file, it is just a comma seperated string which looks like this:

Class-Path: lib/log4j.jar lib/commons.jar

In a project I work on, we keep all our dependencies in a lib directory, where we add and remove libraries from time to time, therefore it would be nice if the above text can be generated automatically. Fortunately, this is possible using just Ant. Unfortunately, this is rather cumbersome. (If you want something more powerful and simpler to use, have a look at Rake).

In a nutshell, this is how I did it:

  1. First, let’s define some properties:
    <!-- name of the output .jar file -->
    <property name="jar.name" value="ourjarfile.jar" />
    
    <!-- base directory for distribution target -->
    <property name="dist.home" value="dist" />
    
    <!-- base directory for compilation targets -->
    <property name="build.home" value="target" />
    
    <!-- The base directory for all libraries (jar) files -->
    <property name="lib.home" value="lib" />
    
  2. Now to the fun part, create a nice .jar file. This generates the MANIFEST.MF but first generates a property libs.project that contains all .jar files, sperated with space. If you later look at the generated MANIFEST.MF you will notice that the paths are wrapped every 72 character, even in the middle of the word. Although this looks like an error, it works correctly.
    <target name="jar" depends="compile" description="Create jar and MANIFEST.MF">
    
      <!-- create a property containing all .jar files, prefix lib/, and seperated with a space -->
      <pathconvert property="libs.project" pathsep=" ">
        <mapper>
          <chainedmapper>
    
            <!-- remove absolute path -->
            <flattenmapper />
    
            <!-- add lib/ prefix -->
            <globmapper from="*" to="lib/*" />
          </chainedmapper>
        </mapper>
    
        <path>
    
          <!-- lib.home contains all jar files, in several subdirectories -->
          <fileset dir="${lib.home}">
            <include name="**/*.jar" />
          </fileset>
        </path>
      </pathconvert>
    
      <!-- create the jar -->
      <jar jarfile="${build.home}/${jar.name}" basedir="${build.home}/classes">
    
        <!-- define MANIFEST.MF -->
        <manifest>
          <attribute name="Built-By" value="${user.name}" />
          <attribute name="Main-Class" value="my.path.to.the.main.Application" />
          <section name="common">
            <attribute name="Specification-Title" value="${component.name}" />
            <attribute name="Specification-Version" value="${component.version}" />
            <attribute name="Specification-Vendor" value="${component.vendor}" />
            <attribute name="Implementation-Title" value="${component.name}" />
            <attribute name="Implementation-Version" value="${component.version} ${TODAY}" />
            <attribute name="Implementation-Vendor" value="${component.vendor}" />
          </section>
    
          <!-- finally, use the magically generated libs path -->
          <attribute name="Class-Path" value="${libs.project}" />
        </manifest>
      </jar>
    </target>
    
  3. Finally we need to create a distribution by copying all .jar files into dist/lib while using a flat directory structure:
    <target name="dist" depends="jar" description="Create binary distribution">
      <delete dir="${dist.home}" />
    
      <!-- contains all library dependencies -->
      <mkdir dir="${dist.home}/lib" />
    
      <copy todir="${dist.home}" file="${build.home}/${jar.name}" />
    
      <copy todir="${dist.home}/lib" filtering="off">
    
        <!-- remove the directory hierarchy: lib contains no subdirectories -->
        <flattenmapper />
        <fileset dir="${lib.home}" includes="**/*.jar" />
      </copy>
    </target>

I use Ant 1.6.2, and this works perfectly for me. Have fun!

15 Responses to “HOWTO Create MANIFEST.MF Classpath From Ant”

  1. HOWTO Create MANIFEST.MF Classpath From Ant on June 28th, 2006 10:55 pm

    [...] 1 votes [...]

  2. PeMuLa BlOg :: Perkenalanku dengan ANT :: July :: 2006 on July 4th, 2006 3:04 pm

    [...] Tapi gimana klo di ant ? Wach puyeng nech ( Setelah tanya-tanya ama pakde google akhirnya dpt link ini: http://martin.ankerl.org/2005/11/30/howto-create-manifestmf-classpath-from-ant/ [...]

  3. brian liddle on February 26th, 2008 5:31 pm

    Is the ClassPath defining the physical directory where the libs must exist? i.e. if ${libs.project} is defined as “c:/devl/workspace/libs”, how will this work for anyone else but the developer that wrote the code?

  4. Martin Ankerl on February 26th, 2008 11:45 pm

    brian, it is a relative path, like

    lib/commons-cli-1.1.jar lib/commons-logging-1.1.jar

    So you just need to copy the generated .jar and the lib directory and everything works.

  5. martin on April 4th, 2008 3:08 pm

    fantastic! you rule!

  6. Michele Silva on July 30th, 2008 9:30 am

    Great! Finally a solution that really works. Not complicated at all ;)

  7. RIF on October 21st, 2008 9:58 pm

    Hey everybody

    Why not just use the core Ant task: “manifestclasspath”.

    It’s in the AntManual

    http://ant.apache.org/manual/index.html

  8. --dnajam on November 25th, 2008 2:48 pm

    Hi dear Martin Ankerl ,
    I have tried your sample code its writing the Manifest.mf file correctly. but while invoking any class file from an external jar, for example StringUtils from apache common-lang. it says java.lang.NoClassDefFoundError: org/apache/commons/lang/StringUtils.
    However, commons-lang-2.4.jar is available within the folder name lib at the root of the jar archive as shown in above example. And the Manifest.MF also have an entry similar to following
    Class-Path: lib/commons-lang-2.4.jar

    Please help
    regards
    –danishnajam

  9. Dan S on April 28th, 2009 6:23 pm

    Thank you, thank you, thank you. This was the answer to several hours of pain. I tried using but couldn’t figure out how to make it build relative paths. (I’m an Ant newbie and couldn’t figure how to pipe the flattenmapper result to the globmapper…chainedmapper was the missing link)

    I suspect dnajam’s errors are due to attempting to load classes from a jar within his project jar. If I understand correctly, that requires a custom classloader. The solution presented here is for referencing external jars placed in a relative path below where the project jar is.

  10. Reynolds on July 1st, 2009 7:33 pm

    Muchas gracias por la publicacion del script justo lo que necesitaba para generar el jar de mi proyecto

  11. Martin Ankerl on July 1st, 2009 9:15 pm

    de nada :)

  12. Dimitri Koami Missoh on July 29th, 2009 10:25 pm

    Thanks a lot for sharing your experience. Your blog entry helps me a lot when I get stuck today trying to make my deployment run.

    Dimitri Missoh.

  13. Bernhard Kabelka on August 20th, 2009 11:45 am

    Thanks Martin for saving my day! When I was facing this problem today at my new job, I remembered our project and your nifty Ant script. However, I already feared the worst, as I have never really taken a closer look on the Ant script. Can you imagine my joy on finding this blog entry? :-)

  14. Martin Ankerl on August 23rd, 2009 4:53 pm

    Hi Bernhard! Service is our success ;)

  15. Venkat on October 15th, 2009 7:22 pm

    Awesome!! It’s perfect and working great. Thanks a lot, Martin.

Leave a Reply