I'm trying to compile my Java program into an executable jar file, and I've tried following all the instructions in various places, but I'm encountering an issue.
The first thing I do is compile everything to class files with this command.
javac -d ./build src/main/**/*.java -cp lib/main/commons-cli-1.4/commons-cli-1.4.jar:lib/main/commons-io-2.8.0/commons-io-2.8.0.jar
This compiles all the .java
files in src/main
(I don't want to compile the tests of course) into a folder called build
. It also sets the classpath to all the .jar
files that the code depends on (Apache Commons CLI and Apache Commons IO).
The next step is to generate the .jar
file. For this, I use the following command (after cd ./build
).
jar cvfm ../bin/program.jar manifest.txt .
Here, manifest.txt
is in the folder build
and contains the following. (It ends with a trailing newline, as per the documentation dictates.)
Main-Class: cli.Cli
Here, cli.Cli
refers to the class named Cli
located in src/main/cli/Cli.java
which contains the main method.
Now I use the following command to try to run the produced program.jar
file, but I get this error.
$ java -jar build/program.jar
Error: Unable to initialize main class cli.Cli
Caused by: java.lang.NoClassDefFoundError: org/apache/commons/cli/ParseException
This error is referring to one of the import statements in src/main/cli/Cli.java
, which is supposed to be in the same .jar
file as all the other import statements. The IDE does recognise the import, but I'm not sure why I am unable to run the file. Here is an extract of my imports from Cli.java
.
package cli;
import java.io.IOException;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.io.FilenameUtils;
// more imports...
I tried to change the manifest to contain a classpath, but that didn't seem to help at all. I tried the following two versions of the manifest.
Relative to bin/
or build/
:
Main-Class: cli.Cli
Class-Path: ../lib/main/commons-cli-1.4/commons-cli-1.4.jar:../lib/main/commons-io-2.8.0/commons-io-2.8.0.jar
Relative to workspace folder (which is the current directory when I run the jar):
Main-Class: cli.Cli
Class-Path: lib/main/commons-cli-1.4/commons-cli-1.4.jar:lib/main/commons-io-2.8.0/commons-io-2.8.0.jar
I changed my code to not use ParseException
but then another of the imports caused the same error.
Turns out the classpath in the manifest needed to be space-separated, with the paths to each jar relative to bin/
, like so:
Main-Class: cli.Cli
Class-Path: ../lib/main/commons-cli-1.4/commons-cli-1.4.jar ../lib/main/commons-io-2.8.0/io-2.8.0.jar program.jar