Can gradle set runtime dependencies from compile dependencies?

I am using bits and pieces from the Java Units of Measurement project in my application. My code compiles when gradle build is invoked, but gradle run fails with a ClassNotFoundException, followed by different UoM classes that I do not use directly.

The specific dependencies I am using are:

compile 'javax.measure:unit-api:0.9'
compile 'systems.uom:systems-common-java8:0.2'
compile 'tec.uom:uom-se:0.9'

but I find I have to add the following to get past the ClassNotFoundExceptions

runtime 'systems.uom:systems-quantity:0.3'
runtime 'si.uom:si-units-java8:0.5'
runtime 'si.uom:si-quantity:0.5'

I figured out the runtime dependencies by looking at the UoM GitHub for the missing class and adding the corresponding package as a runtime dependency then trying gradle run again. While this does work, I feel that this is exactly what Gradle is meant to address. I found this post from the old forums which states that Gradle should already do what I want, but I’m definitely seeing something different or misunderstanding something.

The systems-common-java8 POM already declares dependencies on the packages I had to add, so it isn’t immediately clear to me why they have to be added as an explicit runtime dependency, especially since compile dependencies should be included in runtime dependencies (at least from the topics I read)

What am I missing?

The 'systems.uom:systems-quantity:0.3' dependency is marked as ‘provided’ scope in the POM which is why it’s not being brought in by Gradle. The other two dependencies should have been added, unless you are doing something like disabling transitive dependencies. I’d remove both the other ‘runtime’ dependencies as they should be unnecessary.

If you still have trouble trying running gradle dependencies --configuration runtime and sharing the result.

If I interpreted what you said correctly, this config:

compile 'javax.measure:unit-api:0.9'
compile 'systems.uom:systems-common-java8:0.2'
compile 'tec.uom:uom-se:0.9'
runtime 'systems.uom:systems-quantity:0.3'

produced this result upon running gradle run:

Exception in Application start method
java.lang.reflect.InvocationTargetException
    <stack trace>
Caused by: java.lang.NoClassDefFoundError: javax/measure/quantity/ElectricPermittivity
    at si.uom.SI.<clinit>(SI.java:135)
    at <my application>
    <stack trace>
Caused by: java.lang.ClassNotFoundException: javax.measure.quantity.ElectricPermittivity

I don’t think I disabled transitive dependencies. This is the entirety of the build.gradle file for the above invocation:

apply plugin: 'application'

sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8

mainClassName = 'com.cres.girthweld.gui.Main'

configurations {
    checkerFrameworkAnnotatedJDK {
        description = 'A copy of JDK classes with Checker Framework type qualifiers inserted'
    }
}

dependencies {
    compile 'com.google.guava:guava:19.0'
    compile 'org.slf4j:slf4j-api:1.7.21'
    compile 'org.slf4j:slf4j-simple:1.7.21'

    compile 'javax.measure:unit-api:0.9'
    compile 'systems.uom:systems-common-java8:0.2'
    compile 'tec.uom:uom-se:0.9'
    runtime 'systems.uom:systems-quantity:0.3'

    compile 'org.checkerframework:checker-qual:2.1.0'
    compile 'org.checkerframework:checker:2.1.0'
    checkerFrameworkAnnotatedJDK 'org.checkerframework:jdk8:2.1.0'

    testCompile 'org.testng:testng:6.9.11'
}

repositories {
    jcenter()
}

test.useTestNG()

def checkerList = ['org.checkerframework.checker.nullness.NullnessChecker',
                   'org.checkerframework.checker.regex.RegexChecker',
                   'org.checkerframework.checker.signature.SignatureChecker',
                   'org.checkerframework.checker.guieffect.GuiEffectChecker'
]

gradle.taskGraph.whenReady {graph ->
    if (graph.hasTask(tasks.buildWithCheckers)) {
        compileJava {
            options.debug(debugLevel: 'source,lines,vars')
            options.fork = true
            options.forkOptions.executable = '/Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/bin/javac'
            options.compilerArgs += ['-Xlint:all',
                                     '-Xdoclint:all',
                                     '-Xdoclint:-missing',
                                     '-processor',
                                     "${checkerList.join(',')}",
                                     '-AcheckPurityAnnotations',
                                     '-AinvariantArrays',
                                     '-Alint=all,-debugSpew',
                                     '-AshowSuppressWarningKeys',
                                     '-AcheckCastElementType',
                                     "-Xbootclasspath/p:${configurations.checkerFrameworkAnnotatedJDK.asPath}"
            ]
        }
    }
}
task buildWithCheckers(dependsOn: build) {}

gradle dependencies --configuration runtime gave me this:

rp:girth-weld-thermal-analysis awang$ gradle dependencies --configuration runtime
:dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

runtime - Runtime dependencies for source set 'main'.
+--- com.google.guava:guava:19.0
+--- org.slf4j:slf4j-api:1.7.21
+--- org.slf4j:slf4j-simple:1.7.21
|    \--- org.slf4j:slf4j-api:1.7.21
+--- javax.measure:unit-api:0.9
+--- systems.uom:systems-common-java8:0.2
|    +--- tec.uom:uom-se:0.8 -> 0.9
|    |    +--- javax.measure:unit-api:0.9
|    |    +--- tec.uom.lib:uom-lib-common:0.9
|    |    |    \--- javax.measure:unit-api:0.9
|    |    \--- javax.annotation:javax.annotation-api:1.2
|    +--- si.uom:si-quantity:0.4
|    |    \--- javax.measure:unit-api:0.8 -> 0.9
|    \--- si.uom:si-units-java8:0.4
|         +--- javax.measure:unit-api:0.8 -> 0.9
|         \--- tec.uom:uom-se:0.8 -> 0.9 (*)
+--- tec.uom:uom-se:0.9 (*)
+--- org.checkerframework:checker-qual:2.1.0
+--- org.checkerframework:checker:2.1.0
\--- systems.uom:systems-quantity:0.3
     \--- javax.measure:unit-api:0.9

(*) - dependencies omitted (listed previously)

BUILD SUCCESSFUL

Total time: 1.052 secs

The two packages I unnecessarily marked as runtime dependencies are indeed there, but apparently not on the runtime classpath.

The build still fails after adding back either one of the two runtime dependencies; as far as I can tell, all 3 are needed.

Actually… Could it be a version issue? The two runtime packages are version 0.4 while I specified 0.5.

I think it is a version issue but I think the issue is with javax.measure:unit-api. I think what’s happening is you are compiling against version 0.8 and running against 0.9. Since that API is exposed to your code you should be listing it as a direct dependency anyhow. I’d try adding compile javax.measure:unit-api:0.9 to ensure you are compiling and running against the same thing.

I already have that listed as a compile dependency.

The short answer here is yes, Gradle is doing the correct thing here. I don’t know enough about these particular libraries to make a call but it may very well be the case that you need version 0.5 of si.uom:si-quantity and that is the underlying problem.

Fun fun fun. I assume this is something I need to contact the library author about?

Potentially. The fact there some dependencies are marked ‘provided’ indicates that he library assumes something will be provided by the runtime environment.

Interesting, didn’t know that. Thank you for your help!