frapontillo
frapontillo

Reputation: 10679

Gradle extension as NamedDomainObjectContainer of Closure

I'm trying to build a Gradle plugin that would allow the following:

myPluginConfig {
    something1 {
        // this is a closure
    }
    somethingElse {
        // this is another closure
    }
    // more closures here
}

To achieve this I'm fairly certain I need to use a NamedDomainObjectContainer to wrap a Closure collection, so I've set up the following plugin:

class SwitchDependenciesPlugin implements Plugin<Project> {
    void apply(Project project) {
        // add the extension
        project.getExtensions().myPluginConfig = project.container(Closure)
        // read the current configuration
        NamedDomainObjectContainer<Closure> config = project.myPluginConfig
        // test it out, always returns []
        System.out.println(config)
    }
}

What am I doing wrong, do I need to use project.extensions.create instead? If so, how?

EDIT: my use case consists in adding dependencies according to some variables defined in the project hierarchy. For example, the following configuration would add the red project if the variable red is defined on project.ext, or gson otherwise:

myPluginConfig {
    redTrue {
        compile project(':red')
    }
    redFalse {
        compile 'com.google.code.gson:gson:2.4'
    }
    greenTrue {
        compile project(':green')
    }
}

For this use case I need to have dynamic names for myPluginConfig, and therefore either a Map or a NamedDomainObjectContainer.

Upvotes: 1

Views: 1627

Answers (3)

18446744073709551615
18446744073709551615

Reputation: 16832

2024 update. If you were searching for "nested extensions" or "nested extension objects", or "a collection (set) of extensions", this may be what you are looking for.

The relevant "Maintaining multiple domain objects" chapter of the Gradle docs may be found here or here (both at archive.org). The example is not there in the most recent version of docs.

A really good explanation of what is going on in that confusing example is here (or here, at archive.org).

The example is outdated, but it at least explains how NamedDomainObjectContainer works.

build.gradle:

apply plugin: DocumentationPlugin

books {
    quickStart {
        sourceFile = file('src/docs/quick-start')
    }
    userGuide {

    }
    developerGuide {

    }
}

task books << {
    books.each { book ->
        println "$book.name -> $book.sourceFile"
    }
}

class DocumentationPlugin implements Plugin<Project> {
    void apply(Project project) {
        def books = project.container(Book)
        books.all {
            sourceFile = project.file("src/docs/$name")
        }
        project.extensions.books = books
    }
}

class Book {
    final String name
    File sourceFile

    Book(String name) {
        this.name = name
    }
}

books.all {...} is not iteration, the closure is executed each time a new book is added.

The property name (in Book) is important, NamedDomainObjectContainer is "a set of named objects", it works as a map from names to these named objects, names must be unique.

> gradle -q books
developerGuide -> /home/user/gradle/samples/userguide/organizeBuildLogic/customPluginWithDomainObjectContainer/src/docs/developerGuide
quickStart -> /home/user/gradle/samples/userguide/organizeBuildLogic/customPluginWithDomainObjectContainer/src/docs/quick-start
userGuide -> /home/user/gradle/samples/userguide/organizeBuildLogic/customPluginWithDomainObjectContainer/src/docs/userGuide

What you are expected to see above is that the paths for developerGuide and userGuide end with, correspondingly, developerGuide and userGuide, because this is what books.all { ... } specified, while the path for quickStart ends with quick-start.

Upvotes: 0

tasomaniac
tasomaniac

Reputation: 10342

You cannot have Closure as a type for NamedDomainObjectContainer simply because the type you use must have a property called name and a public constructor with a single String parameter.

To overcome this, you may create a wrapper around Closure with a name field added.

Upvotes: 0

Rene Groeschke
Rene Groeschke

Reputation: 28653

Can you elaborate what you try to model here? I think you have two options. One is to use NamedDomainObjectContainer. Here you need a class that represents "something". Have a look at the Gradle userguide chapter about maintaining multiple domain objects (see https://docs.gradle.org/current/userguide/custom_plugins.html#N175CF) in the sample of the userguide, the "thing" is 'Book'. The build-in configuration syntax like you described above comes for free.

If you want to have a syntax like above without the need for maintaining multiple domain objects, you can simply add a method that takes a Closure as a parameter to your Extension class:

void somethingToConfigure(Closure) {
}

Upvotes: 1

Related Questions