Reputation: 10679
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
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
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
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