Hello,
I maintain a binary plugin written in Java. It allows to integrate the build of a Javascript application inside a Gradle multi-projects build. Most of dependencies used by my plugin are applied in the implementation
configuration, and consequently, they have a runtime
scope in the POM artifact published. That means there’s no guarantee the same version of these dependencies is selected in end-user projects, and there may be dependency conflicts. However, I noticed the most recent version selection strategy does not take into account the version of the dependency used by my plugin (see hereafter).
To prevent conflicts, I usually recommend to isolate projects in subprojects so as the root project does not apply any plugin and/or dependencies. But I cannot explain why to end-users, because I cannot find neither official guidance in Gradle docs about that, nor a detailed explanation of how plugin dependencies are taken into account during the dependency resolution process, and their scope within the whole project.
When I explore end-user project layouts, there are 3 typical setups:
LAYOUT 1
An empty root project with a Java subproject applying plugins and dependencies, and a subproject applying my plugin only:
root-project
|__ settings.gradle.kts // include("subproject1", "subproject2")
|__ subproject1
|__ build.gradle.kts
|__ java
|__ plugin(<another-plugin-id>) version "<version>"
|__ subproject2
|__ build.gradle.kts
|__ plugin(<my-plugin-id>) version "<my-version>"
With this layout, my plugin is unlikely to suffer a conflict with one of its dependencies: each subproject does not seem to interfere with each other.
LAYOUT 2
A Java root project applying plugins and dependencies, and a subproject applying my plugin only:
root-project
|__ settings.gradle.kts // include("subproject")
|__ build.gradle.kts
|__ java
|__ plugin(<another-plugin-id>) version "<version>"
|__ subproject
|__ build.gradle.kts
|__ plugin(<my-plugin>) version "<my-version>"
With this layout, sometimes, end-users report a dependency conflict when my plugin executes one of its task. If I refactor the root project to get something like the first layout, the dependency conflict does not appear anymore.
LAYOUT 3
A root project applying all plugins and dependencies:
root-project
|__ build.gradle.kts
|__ java
|__ plugin(<another-plugin-id>) version "<version>"
|__ plugin(<my-plugin>) version "<my-version>"
With this layout, there are sometimes dependency conflicts that I understand perfectly because plugins and dependencies share the same classpath.
Q1. What is the recommended project layout: 1, 2, 3?
Q2. How a plugin, its dependencies, and Java dependencies defined in a root project apply to subprojects?
Q3. In layout 2, though my plugin uses the most recent version of a dependency, it seems an older and incompatible version in the root project is selected and still applied in the subproject. Am I mistaken?
Q4. In layout 2, the dependencyInsight
task does not help to identify the cause of a conflict occuring inside a plugin task execution because it focuses on configurations rather than on plugin dependencies. How to efficiently deal with such issue in this case?
Thanks a lot for your help!
BR