What is the convention for project inheritance with regards to custom plugins?

This is not a question about how, but a question about standards or conventions when writing custom plugins for Gradle.

If a custom plugin ‘A’ is applied to both project ‘:foo’ and subproject ‘:foo:bar’, should plugin ‘A’ in ‘:foo:bar’ ever inherit any data from plugin ‘A’ in ‘:foo’?

Here is a more concrete example. In Maven, I am able to declare a dependency management block in a parent project. Dependency management in this form is convenient for standardizing on versions of dependencies in a multi-project build:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
         <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.13</version>
        </dependency>
         <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.1.12.Final</version>
        </dependency>
         <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>3.2.3.RELEASE</version>
        </dependency>
         <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.2.3.RELEASE</version>
        </dependency>
         <dependency>
            <groupId>org.springframework.ws</groupId>
            <artifactId>spring-ws-core</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>
         <dependency>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
            <version>2.8.1</version>
        </dependency>
    </dependencies>
</dependencyManagement>

This dependency management block is inherited by all subprojects in Maven and provides a convenient way to manage the versions of dependencies between projects. Admittedly, the only value a dependency management block has is in a multi-project build.

The difference between a dependency management block and a simple dependency block in the parent project is the inclusion of the actual dependencies in subprojects. The dependency management block does not actually include the dependency in the subprojects, but only supplies the version/exclusion information for matching dependencies declared by the subproject. The dependency block actually includes all of the dependencies in the subproject.

There are two great Gradle plugins that attempt provide this Maven-like dependency management behavior. However, they have differing views of how the dependency management block is conveyed to subprojects.

The dependency-management-plugin from spring-gradle-plugins takes the view point that plugins are applied on a per-project basis including the data maintained by the plugin. The data maintained in the dependency managment block is not inherited by subprojects, but may be apply directly to subprojects using the subprojects block.

subprojects {

apply plugin: ‘io.spring.dependency-management’

dependencyManagement {

dependencies {

‘log4j:log4j’ ‘1.2.17’

‘org.freemarker:freemarker’ ‘2.3.13’

‘org.hibernate:hibernate-core’ ‘4.1.12.Final’

‘org.springframework:spring-beans’ ‘3.2.3.RELEASE’

‘org.springframework:spring-core’ ‘3.2.3.RELEASE’

org.springframework.ws:spring-ws-core’ ‘2.1.2.RELEASE’

‘xerces:xercesImpl’ ‘2.8.1’

}

}

}

The gradle-dependency-management-plugin from sblundy provides dependency management takes the view point that plugins are applied on a per-project basis but may share the data maintained by the plugin. The data contained in the dependency managment block is inherited by subprojects using the dependency-management plugin.

apply plugin: ‘dependency-management’

dependencyManagement {

dependency(‘log4j:log4j:1.2.17’)

dependency(‘org.freemarker:freemarker:2.3.13’)

dependency(‘org.hibernate:hibernate-core:4.1.12.Final’)

dependency(‘org.springframework:spring-beans:3.2.3.RELEASE’)

dependency(‘org.springframework:spring-core:3.2.3.RELEASE’)

dependency(‘org.springframework.ws:spring-ws-core:2.1.2.RELEASE’)

dependency(‘xerces:xercesImpl:2.8.1’)

}

Both of these accomplish the same goal, but one plugin references the parent project in order to accomplish the task while the other one relies on the subprojects block to apply the block to multiple projects.

The question is simply this: Should Gradle plugins ignore project hierarchy and avoid referencing data from a plugin of the same type in a parent project?

I just want to point out I find it amusing that the Spring plugin takes the inversion of control approach to this problem. How appropriate.

That being said, I personally prefer that approach. I feel it’s a bit more explicit and clear where the subproject plugin is getting its configuration from. Also with the Spring plugin I could see the possibility to define separate dependency management configurations for different subprojects, I don’t believe the approach taken by the latter plugin would allow for that kind of flexibility.

I’m the author of the dependency-management-plugin from spring-gradle-plugins.

I’ve tried to follow the precedent that, in my opinion, is set elsewhere in Gradle and chose not to inherit configuration. An example of configuration not being inherited can be seen in Gradle’s handling of dependencies:

allprojects {
 apply plugin: 'java'
   repositories {
  mavenCentral()
 }
}
  dependencies {
 compile 'org.springframework:spring-core:4.1.1.RELEASE'
}
  project('sub') {
  }

In this case the root project has a dependency on spring-core, but the sub project does not

I totally agree. The Spring plugin way is the Gradle way.