How to handle run-time dependencies

When gradle, with the help of jitpack, builds the JAR, there are dependencies:

thufir@mordor:~/NetBeansProjects/hello_client$ 
thufir@mordor:~/NetBeansProjects/hello_client$ jar -i build/libs/hello_client.jar 
java.io.FileNotFoundException: build/libs/hello_api-dev.jar (No such file or directory)
    at java.util.zip.ZipFile.open(Native Method)
    at java.util.zip.ZipFile.<init>(ZipFile.java:219)
    at java.util.zip.ZipFile.<init>(ZipFile.java:149)
    at java.util.jar.JarFile.<init>(JarFile.java:166)
    at java.util.jar.JarFile.<init>(JarFile.java:103)
    at sun.tools.jar.Main.getJarPath(Main.java:1163)
    at sun.tools.jar.Main.getJarPath(Main.java:1179)
    at sun.tools.jar.Main.genIndex(Main.java:1195)
    at sun.tools.jar.Main.run(Main.java:317)
    at sun.tools.jar.Main.main(Main.java:1288)
thufir@mordor:~/NetBeansProjects/hello_client$ 

So, I copied the missing file into build/libs:

thufir@mordor:~/NetBeansProjects/hello_client$ 
thufir@mordor:~/NetBeansProjects/hello_client$ jar -i build/libs/hello_client.jar 
java.io.FileNotFoundException: build/libs/hello_api-dev.jar (No such file or directory)
    at java.util.zip.ZipFile.open(Native Method)
    at java.util.zip.ZipFile.<init>(ZipFile.java:219)
    at java.util.zip.ZipFile.<init>(ZipFile.java:149)
    at java.util.jar.JarFile.<init>(JarFile.java:166)
    at java.util.jar.JarFile.<init>(JarFile.java:103)
    at sun.tools.jar.Main.getJarPath(Main.java:1163)
    at sun.tools.jar.Main.getJarPath(Main.java:1179)
    at sun.tools.jar.Main.genIndex(Main.java:1195)
    at sun.tools.jar.Main.run(Main.java:317)
    at sun.tools.jar.Main.main(Main.java:1288)
thufir@mordor:~/NetBeansProjects/hello_client$ 

thufir@mordor:~/NetBeansProjects/hello_client/build/libs$ 
thufir@mordor:~/NetBeansProjects/hello_client/build/libs$ cp /home/thufir/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy-all/2.4.1/a9ca9c9de09361ec2a18d2c058d2524fbd8eae0c/groovy-all-2.4.1.jar hello_api-dev.jar hello_client.jar .
cp: ‘hello_api-dev.jar’ and ‘./hello_api-dev.jar’ are the same file
cp: ‘hello_client.jar’ and ‘./hello_client.jar’ are the same file
thufir@mordor:~/NetBeansProjects/hello_client/build/libs$ 
thufir@mordor:~/NetBeansProjects/hello_client/build/libs$ jar -i hello_client.jar 
thufir@mordor:~/NetBeansProjects/hello_client/build/libs$ 
thufir@mordor:~/NetBeansProjects/hello_client/build/libs$ ll
total 6816
drwxrwxr-x 2 thufir thufir    4096 Mar 11 03:41 ./
drwxrwxr-x 6 thufir thufir    4096 Mar 11 03:30 ../
-rw-rw-r-- 1 thufir thufir 6937913 Mar 11 03:41 groovy-all-2.4.1.jar
-rw-rw-r-- 1 thufir thufir    1422 Mar 11 03:39 hello_api-dev.jar
-rw-rw-r-- 1 thufir thufir    2722 Mar 11 03:41 hello_client.jar
thufir@mordor:~/NetBeansProjects/hello_client/build/libs$ 
thufir@mordor:~/NetBeansProjects/hello_client/build/libs$ java -jar hello_client.jar 
hello [fred]
thufir@mordor:~/NetBeansProjects/hello_client/build/libs$ 

How do I build hello_client.jar so that hello_api-dev.jar is also there, so that client won’t crash? Ideally, I would want gradle to include any JAR’s marked as runtime dependencies in the build directory.

Is this a correct, or normal practice? Why or why not?

My current build file:

apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'maven'

mainClassName = 'net.bounceme.mordor.hello.client.HelloClient'

sourceCompatibility = '1.8'
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
if (!hasProperty(mainClassName)) {
    ext.mainClass = mainClassName
}

repositories {
    mavenCentral()
    maven { url "https://jitpack.io" }
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.10'
    compile 'com.github.THUFIR:hello_api:dev'
}

jar {
    manifest {
        attributes ('Main-Class': mainClassName,
            "Class-Path": configurations.compile.collect { it.getName() }.join(' '))
    }
}

assemble.dependsOn (jar)

configurations.all {
    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}

for these repos:


First an important question: Why don’t you just use the gradle run task or the distributions built by the application plugin? They will work out of the box without any manifest or classpath magic.

Only If you really want a fat jar with inlined dependencies, have a look at the shadow plugin or this stackoverflow question.

This will resolve the dependencies at configuration time, making all your builds slower, don’t do this. See this forum post for an explanation and solution.

Why are you doing this?

This is the default behavior, no need to repeat it.

Just a quick reply with followup questions.

for the gradle run task, how does it even know what to add to the classpath for a run-time dependency? My thinking was that, whatever classpath run is using, to add those JAR’s so that they’re alongside the finished JAR.

I should look more closely at the install scripts. My thinking was that I don’t want an OS specific way of installing, just CLI using the java command.

I don’t think that I’m after a fat JAR. When I’ve used Netbeans with Ant, it builds the JAR, and then in the same directory adds a libs folder for run time dependencies. I wouldn’t, naively, call that a “fat” JAR.

I was looking at
https://docs.gradle.org/current/dsl/org.gradle.language.assembler.tasks.Assemble.html

but will have to experiment as to what this actually does.

Dollars to donuts that gradles does what I want out of the box, I’m just not hitting the right keys, so to speak. Correct commands. The installDist looks interesting, will try that and the install scripts.

That’s exactly what the distZip task from the application plugin does. And it adds convenient start scripts so you don’t have to write the long java command yourself.

I have to manually extract the JAR’s from the zip file. Also, the hierarych seems odd to me. It’s normal for the JAR’s to extract to the same directory? I would assume that there would be a libs directory for the run-time dependencies?

I would think that the normal structure would be to use a libs directory?

Class-Path: lib/one.jar lib/two.jar

Or is it normal to put those jars in the working directory? I suppose you can do it however you like, at the end of the day, but what would be the standard approach?

You can use gradle run when you want to try it out locally. For distribution you need to pack it in a zip anyway.

There is no “standard structure” for Java applications. But any structure you want is just a Copy or Zip task away, so feel free to create what you prefer :slight_smile: