Use ANT to auto-generate the list of jar files in a MANIFEST.MF

Writing a manifest by hand is both error prone & boring. Moreover, if you change the dependancies, add/remove libraries jars from you project, then you have to modify again the manifest file…
One method is to use ANT to create the manifest for you, and more specifically the “Class-Path” line in the manifest.
The following exemple speaks for itself and generate a manifest file as listed in my previous post about jar files (https://fraaargh.wordpress.com/2008/07/13/how-to-java-jars-and-manifestmf):

<path id=”library.Project”>
<fileset dir=”../lib/”>
<include name=”**/*.jar”/>
</fileset>
</path>

<!– This is the important line: by specifying in the “jarfile” attribute the location of the
generated jar, ANT will be able to deduce the relative path to you jar libraries –>
<manifestclasspath property=”lib.list” jarfile=”.”>
<classpath refid=”library.Project” />
</manifestclasspath>

<!– Compile –>
<target name=”compile” depends=”build.env” description=”Compiles tmmerge Java source code.”>
<mkdir dir=”${build.dir}”/>
<javac srcdir=”${src.dir}” destdir=”${build.dir}” deprecation=”true” debug=”true” optimize=”true”>
<classpath>
<path refid=”class.path” />
</classpath>
</javac>

<!– JAR –>
<jar jarfile=”${SOFT_PACKAGE}/${software.name}” basedir=”${build.dir}”>
<manifest>
<attribute name=”Class-Path” value=”${lib.list}”/>
<attribute name=”Main-Class” value=”fr.alcatel.ascc.tmmerge.TMMerge”/>
<attribute name=”Product-Name” value=”TMMerge”/>
<attribute name=”Package-Title” value=”fr.alcatel.ascc.tmmerge”/>
<attribute name=”Package-Version” value=”${software.version}”/>
</manifest>

</jar>
</target>

Advertisements

How-to java jars and MANIFEST.MF

Here’s a brief “how-to” for remembering what is possible with a jar file, how to use it, launch its main class and define where the other jars (the libraries used) are stored. I could not find anywhere on the net a good explanation of all this, so decided to write my own.
So imagine you have your own jar file named myApp.jar which main class (the one with the “main” method) is marot.francois.MyMainClass which needs to receive 2 arguments: arg1 and arg2 (passed in String[] args).

1- Without using any MANIFEST.MF fil

If you don’t have a MANIFEST.MF file in your jar, or if you want to set some specific places where Java should look for the libraries used by myApp.jar, then you should use the following command. Beware, the use of the star is only available since Java 6 (or is it Java 5 ?)

on Windows:
java -cp tmmerge.jar;myCustomLibPath1\*;myCustomLibPath2\*;myCustomLibPath3\* marot.francois.MyMainClass arg1 arg2

on Linux:
java -cp tmmerge.jar:myCustomLibPath1\*:myCustomLibPath2\*:myCustomLibPath3\* marot.francois.MyMainClass arg1 arg2

Same as previous one but with all the libraries’ jars listed instead of using *:
on Windows:
java -cp ./myApp.jar;myCustomLibPath1\sctm.jar;myCustomLibPath2\ant.jar;myCustomLibPath2\ant-launcher.jar;myCustomLibPath3\scdata.jar marot.francois.MyMainClass arg1 arg2

on Linux:
java -cp ./myApp.jar:myCustomLibPath1\sctm.jar:myCustomLibPath2\ant.jar:myCustomLibPath2\ant-launcher.jar:myCustomLibPath3\scdata.jar marot.francois.MyMainClass arg1 arg2

At first, I thought such a command line was overriding the classpath in MANIFEST.MF, if any. But that is not the case, the MANIFEST.MF is just bypassed.


2- Using a MANIFEST.MF file in the jar

Launch myApp using the jar files listed in myApp.jar’s own manifest
java -jar myApp.jar arg1 arg2

The content of the META-INF\MANIFEST.MF file must be:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.0
Created-By: 1.6.0_02-b06 (Sun Microsystems Inc.)
Main-Class: marot.francois.MyMainClass
Product-Name:  myApp
Package-Title: marot.francois
Package-Version: 1.00.00
Package-Vendor: Elsys Design Avisto
Class-Path: myCustomLibPath2/ant-launcher.jar myCustomLibPath2/ant.jar myCustomLibPath3/scdata.jar myCustomLibPath1/sctm.jar

In a next post, I’ll show you how to use ANT to automatically generate the content listed here. And more specifically the list of libraries which would be error prone to write by hand.
By the way, I think it’s good to point to something important: in the “Class-Path”line of the manifest, you CAN’T use a star as you could do when specifying the classpath on the command line.
Also be aware that the space is the jars path delimiters as opposed to the “;” (windows) or “:” (Linux) on the command line. This last remark is very very error prone…

3- What I would like to be able to do (but it seems like Java doesn’t handle this case)

Warning: the exemple given hereafter does not work. I spent a lot of time trying to find some explanation of the reasons why, but was not able to find.
So the problem seems to be that you can’t override the default classpath specified in the jar’s manifest if you use the -cp switch at the same time. So the basic rule of thumb is:
“in java, do not use both switches -jar and -cp at the same time”: it does not work (at least up to Java 6).
I thought it would be cool to be able to run a jar but defining another place where it can find its dependancies. In case you want the users of your app to be able to use a shared folder containing the lib jars but don’t want him to have to know the main class’ name.
Here’s the command line I desperatly tryed to run:
REM java -cp myCustomLibPath1\*;myCustomLibPath2\*;myCustomLibPath3\* -jar myApp.jar arg1 arg2

Nevertheless, the same goal can be reached (successfuly) by using method 1. Only that the user needs to know the main class.