My gradle project contains 3 sub-projects with one source file each:
root-project\
sub-project-abstract\
...AbstractFoo.java
sub-project-commons\
...ConcreteFoo.java (extends AbstractFoo)
sub-project-main\
...Main.java (instantiates ConcreteFoo)
build.gradle of sub-project-commons
:
dependencies {
implementation(project(:sub-project-abstract))
}
build.gradle of sub-project-main
:
dependencies {
implementation(project(:sub-project-commons))
}
The Main-class in sub-project-main
is aware of ConcreteFoo
, however, compilation fails with cannot access AbstractFoo
.
For some reason, I expected sub-project-commons
to "export" ConcreteFoo
and AbstractFoo
, since it's a implementation-dependency. Or in other words, form the perspective of sub-project-main
, AbstractFoo
is a transitive dependency.
However, this doesn't seem to be the case.
I know that I could probably make it work by explicitly adding sub-project-abstract
as a direct dependency to sub-project-main
. However, that's something I want to avoid due to the nature of the commons project (my actual project contains up to 10 subprojects, and it should be possible to reuse the commons-project without declaring a dependency to sub-project-abstract
every single time the commons-project is referenced.
Is there a way to make the Main-class aware of AbstractFoo
without directly declaring sub-project-abstract
as a dependency (but indirectly via sub-project-commons
)?
CodePudding user response:
This is expected behavior for the implementation
configuration. You should apply the Java Library Plugin and use the api
configuration.
The key difference between the standard Java plugin and the Java Library plugin is that the latter introduces the concept of an API exposed to consumers. A library is a Java component meant to be consumed by other components. It’s a very common use case in multi-project builds [emphasis added], but also as soon as you have external dependencies.
The plugin exposes two configurations that can be used to declare dependencies:
api
andimplementation
. Theapi
configuration should be used to declare dependencies which are exported by the library API, whereas theimplementation
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 theimplementation
configuration will, on the other hand, not be exposed to consumers, and therefore not leak into the consumers' compile classpath. [...]
In sub-project-commons
(Kotlin DSL):
plugins {
...
`java-library`
}
...
dependencies {
api(project(":sub-project-abstract"))
}
...