0
votes

I am trying to move the code from a set of several plugins written in Gradle scripts included with "apply from: " to a jar of Groovy scripts included with "apply plugin: ". I have been mostly successful but I am having trouble creating 'extension objects' for those plugins that have them. I seem to be unable to reference the extension class name within the plugin's apply function. I am missing something basic - what is it?

I have whittled the problem down to a simple example that I hope will serve as a useful template. Here is a plugin named "sample":

PLUGIN: plugin\src\main\groovy\com\company\gradle\sample.groovy:

package com.company.gradle

import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.Plugin
import org.gradle.api.Task

//  Configuration object (project may override these values)
class sampleExtension {
  String product   //  product code - 3 alphanumeric characters
  String customer  //  customer id - integer 0-999999

  sampleExtension() {
    this.product  = 'ABC'
    this.customer = '123'
  }
}

class sample implements Plugin<Project> {

  void apply(Project target) {

println "SAMPLE: A"
    //  create the configuration objects for this plugin
    target.extensions.create('sample', sampleExtension)
println "SAMPLE: B"
    //  This task generates the license.
    Task sampleTask = target.task('printProperties') {  
      doFirst {
        println("target.sample.product  = '$target.sample.product'")
        println("target.sample.customer = '$target.sample.customer'")
      }
    }
println "SAMPLE: C"
  }
}

The printlns are helpful because in the project that uses sample, plugin errors are reported as occurring at some line within the project script, not the plugin script. I'm not sure if the constructor is necessary; I have also tried nesting the extension class inside class 'sample'

plugin\src\main\resources\META-INF\gradle-plugins\sample.properties:

implementation-class=com.company.gradle.sample

plugin\build.gradle

apply plugin: 'groovy'
apply plugin: 'maven'

dependencies {
  compile gradleApi()
  compile localGroovy()
}

group   = 'com.company.gradle'
version = '1.0'

uploadArchives {
  repositories { mavenDeployer { repository(url: uri('../repo')) }}
}

That is the plugin. Here is the project script, which for the purpose of this example has no code - just execute the task provided by the sample plugin:

project\build.gradle:

buildscript {
  repositories{ maven{url uri('../repo')} }
  dependencies { classpath 'com.company.gradle:plugin:1.0' }
}

apply plugin: 'sample'

The plugin is built with 'gradle build uploadArchives' and it compiles clean. The project is built with 'gradle printProperties'. It prints the "A" message, and complains that it failed to apply the sample plugin because 'no such property: sampleExtension' for class 'com.company.gradle.sample'.

c:\jdev\project>gradle printProperties
SAMPLE: A

FAILURE: Build failed with an exception.

* Where:
Build file 'C:\jdev\project\build.gradle' line: 9

* What went wrong:
A problem occurred evaluating root project 'project'.
> Failed to apply plugin [id 'sample']
   > No such property: sampleExtension for class: com.company.gradle.sample

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug
option to get more log output.

BUILD FAILED

Total time: 2.13 secs

The plugin code worked when the plugin was written as a 'sample.groovy' script included in the project\build.gradle with 'apply from: sample.groovy'.

When my extension class was nested, the failure was different: 'could not create an instance of type com.company.gradle.sample$sampleExtension_Decorated.'

What is the pattern when you have an extension object and the plugin is written in .groovy for a plugin jar, rather than .gradle for direct inclusion?

Thanks for taking the time to wade through all this...

1

1 Answers

1
votes

Using a slightly modified version of your code I'm able to get the values of the model.

build.gradle:

group 'com.jbirdvegas.so'
version '1.0-SNAPSHOT'

apply plugin: 'groovy'
// apply our plugin... calls SamplePlugin#apply(Project)
apply plugin: 'sample'

repositories {
    mavenCentral()
}

dependencies {
    compile localGroovy()
}

// caller populates the extension model applied above
sample {
    product = 'abc'
    customer = 'zyx'
}

// dummy task to limit console output for example
task doNothing <<{}

buildSrc/src/main/groovy/com/jbirdvegas/so/a3850424/SamplePlugin.groovy:

package com.jbirdvegas.so.a38504024

import org.gradle.api.Plugin
import org.gradle.api.Project

class SamplePlugin implements Plugin<Project> {
    @Override
    void apply(Project target) {
        // create our extension on the project for our model
        target.extensions.create('sample', SampleModel)
        // once the script has been evaluated the values are available
        target.afterEvaluate {
            // here we can do whatever we need to with our values
            println "populated model: $target.extensions.sample"
        }
    }
}

buildSrc/src/main/groovy/com/jbirdvegas/so/a38504024/SampleModel.groovy:

package com.jbirdvegas.so.a38504024
// define our DSL model
class SampleModel {
    public String product;
    public String customer;

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("SampleModel{");
        sb.append("product='").append(product).append('\'');
        sb.append(", customer='").append(customer).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

buildSrc/src/main/resources/META-INF/gradle-plugins/sample.properties

implementation-class=com.jbirdvegas.so.a38504024.SamplePlugin

Using this setup we can see the values supplied by the caller in your DSL block

$ ./gradlew -q doNothing
SampleModel{product='abc', customer='zyx'}