How to set a long Java classpath in Windows?
Solution 1:
The Windows command line is very limiting in this regard. A workaround is to create a "pathing jar". This is a jar containing only a Manifest.mf
file, whose Class-Path
specifies the disk paths of your long list of jars, etc. Now just add this pathing jar to your command line classpath. This is usually more convenient than packaging the actual resources together.
As I recall, the disk paths can be relative to the pathing jar itself. So the Manifest.mf
might look something like this:
Class-Path: this.jar that.jar ../lib/other.jar
If your pathing jar contains mainly foundational resources, then it won't change too frequently, but you will probably still want to generate it somewhere in your build. For example:
<jar destfile="pathing.jar">
<manifest>
<attribute name="Class-Path" value="this.jar that.jar ../lib/other.jar"/>
</manifest>
</jar>
Solution 2:
Since Java 6 you can use classpath wildcards.
Example: foo/*
, refers to all .jar files in the directory foo
- this will not match class files (only jar files). To match both use:
foo;foo/*
orfoo/*;foo
. The order determines what is loaded first. - The search is NOT recursive
Solution 3:
Use An "Argument File" on Java 9+
In Java 9+, the java executable supports providing arguments via a file. See https://docs.oracle.com/javase/9/tools/java.htm#JSWOR-GUID-4856361B-8BFD-4964-AE84-121F5F6CF111.
This mechanism is explicitly intended to solve the problem of OS limitations on command lengths:
You can shorten or simplify the java command by using @argument files to specify a text file that contains arguments, such as options and class names, passed to the java command. This let’s you to create java commands of any length on any operating system.
In the command line, use the at sign (@) prefix to identify an argument file that contains java options and class names. When the java command encounters a file beginning with the at sign (@) , it expands the contents of that file into an argument list just as they would be specified on the command line.
This is the "right" solution, if you are running version 9 or above. This mechanism simply modifies how the argument is provided to the JVM, and is therefore 100% compatible with any framework or application, regardless of how they do classloading i.e. it is completely equivalent to simply providing the argument on the command line as usual. This is not true for manifest-based workarounds to this OS limitation.
An example of this is:
Original command:
java -cp c:\foo\bar.jar;c:\foo\baz.jar
can be rewritten as:
java @c:\path\to\cparg
where c:\path\to\cparg
is a file which contains:
-cp c:\foo\bar.jar;c:\foo\baz.jar
This "argument file" also supports line continuation characters and quoting for properly handling spaces in paths e.g.
-cp "\
c:\foo\bar.jar;\
c:\foo\baz.jar"
Gradle
If you are encountering this issue in Gradle, see this plugin, which converts your classpath automatically into an "argument file" and provides that to the JVM when doing exec or test tasks on Windows. On Linux or other operating systems it does nothing by default, though an optional configuration value can be used to apply the transformation regardless of OS.
https://github.com/redocksoft/classpath-to-file-gradle-plugin
(disclaimer: I am the author)
See also this related Gradle issue -- hopefully this capability will eventually be integrated into Gradle core: https://github.com/gradle/gradle/issues/1989.