Search code examples
gradlegradle-kotlin-dsltransitive-dependency

In gradle dependencies, why API configurations discards all transitive dependencies?


I have a gradle project that contains 2 subprojects: common & demo.

The common project depends on a published library:

dependencies {

    implementation("eu.timepit:singleton-ops_${vs.scalaBinaryV}:0.5.0")
...

The demo project depends on common as part of its API:

dependencies {

    api(project(":common")) {
        isTransitive =true
    }
}

When I compile both, I observe the correct dependency in common:

compileClasspath - Compile classpath for source set 'main'.
+--- org.scala-lang:scala-compiler:2.12.11
|    +--- org.scala-lang:scala-library:2.12.11
|    +--- org.scala-lang:scala-reflect:2.12.11
|    |    \--- org.scala-lang:scala-library:2.12.11
|    \--- org.scala-lang.modules:scala-xml_2.12:1.0.6
+--- org.scala-lang:scala-library:2.12.11
+--- org.scala-lang:scala-reflect:2.12.11 (*)
\--- eu.timepit:singleton-ops_2.12:0.5.0
     +--- org.scala-lang:scala-compiler:2.12.8 -> 2.12.11 (*)
     +--- org.scala-lang:scala-library:2.12.8 -> 2.12.11
     \--- com.chuusai:shapeless_2.12:2.3.3
          +--- org.scala-lang:scala-library:2.12.4 -> 2.12.11
          \--- org.typelevel:macro-compat_2.12:1.1.1
               \--- org.scala-lang:scala-library:2.12.0 -> 2.12.11

but in demo, the transitive dependecies under common are empty!

compileClasspath - Compile classpath for source set 'main'.
+--- project :common
+--- org.scala-lang:scala-compiler:2.12.11
...

This leads to the very common classpath missing errors, like:

[Error] /xxx/DoubleVectorDemo.scala:9: Symbol 'type shapeless.ProductArgs' is missing from the classpath.
This symbol is required by 'object edu.umontreal.kotlingrad.shapesafe.tensor.DoubleVector'.
Make sure that type ProductArgs is in your classpath and check for conflicting dependencies with `-Ylog-classpath`.
A full rebuild may help if 'DoubleVector.class' was compiled against an incompatible version of shapeless.

So what's the point of dropping libraries for something that is part of your API? And how to override this behaviour in all projects?


Solution

  • From the gradle java-plugin doc,

    The api configuration should be used to declare dependencies which are exported by the library API, whereas the implementation configuration should be used to declare dependencies which are internal to the component. Dependencies appearing in the api configurations will be transitively exposed to consumers of the library, and as such will appear on the compile classpath of consumers. Dependencies found in the implementation configuration will, on the other hand, not be exposed to consumers, and therefore not leak into the consumers' compile classpath

    Let's say of you want to expose eu.timepit:singleton-ops_${vs.scalaBinaryV}:0.5.0 to all of the common library then you need to add this as api dependency in common module build.gradle.kts.

    dependencies {
    
    api("eu.timepit:singleton-ops_${vs.scalaBinaryV}:0.5.0")