I'm trying to write a DSL like the following:
toolSetups {
someTool {
argsFile = file('run/some_tool/xrun_files.f')
}
someOtherTool {
argsFile = file('run/some_other_tool/xrun_files.f')
}
}
I do this by adding an extension to the project, which is a container for ToolSetup
entries:
extensions.add('toolSetups', project.container(ToolSetup))
I followed the examples in the user guide and declared the argsFile
property in ToolSetup
:
interface ToolSetup {
Property<File> getArgsFile()
}
I searched around and found that this only works if ToolSetup
has a name
property. I reworked ToolSetup
to be:
abstract class ToolSetup {
private final String name
public ToolSetup(String name) {
this.name = name
}
String getName() {
return name
}
abstract Property<File> getArgsFile()
}
I'm getting the following:
Cannot set the value of read-only property 'argsFile' for object of type ToolSetup$Inject.
I also tried explicitly calling argsFile.set(file(...))
, but this gives me the same error. I also tried to explicitly create Property
instances using ObjectFactory
, but still got the same.
I already wrote a plugin that does something similar, where it works, but that plugin is written in Java. I'm guessing there's some trivial mistake here related to Groovy and letting Gradle manage the implementation for me, which I'm not seeing. I'd be very grateful if someone could point it out.
Here's the entire build.gradle
file that reproduces this issue:
abstract class ToolSetup {
private final String name
public ToolSetup(String name) {
this.name = name
}
String getName() {
return name
}
abstract Property<File> getArgsFile()
}
extensions.add('toolSetups', project.container(ToolSetup))
toolSetups {
someTool {
argsFile = file('run/some_tool/xrun_files.f')
}
someOtherTool {
argsFile = file('run/some_other_tool/xrun_files.f')
}
}
CodePudding user response:
I was able to get the below to work
buildSrc/src/main/java/com/sample/Resource.java
package com.sample;
import org.gradle.api.provider.Property;
import java.net.URI;
public interface Resource {
// Type must have a read-only 'name' property
String getName();
Property<URI> getUri();
Property<String> getUserName();
}
build.gradle
extensions.add("download", project.objects.domainObjectContainer(com.sample.Resource.class))
download {
register('t1') {
uri = uri("http://abc")
userName = "t1u"
}
register('t2') {
uri = uri("https://abc")
userName = "t2u"
}
}
println "download info "
download.each { println String.format("{name: %s, uri: '%s', userName: %s", it.getName(), it.getUri().getOrNull(), it.getUserName().getOrNull()) }
the output produces
> Configure project :
download info
{name: t1, uri: 'http://abc', userName: t1u
{name: t2, uri: 'https://abc', userName: t2u
BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 up-to-date
CodePudding user response:
There seems to be a bug in Gradle w.r.t. the injected implementation for the toolSetups
container: https://github.com/gradle/gradle/issues/23525
Using project.objects.domainObjectContainer()
to create the container, instead of project.container()
works.
To define the extension, we have to do:
extensions.add('toolSetups', objects.domainObjectContainer(ToolSetup))
Note, it's not necessary to give Gradle an abstract class for ToolSetup
. It can also be supplied an interface, as long as that interface contains a name
property. It will be able to inject a constructor by itself. The following is sufficient:
interface ToolSetup {
String getName()
Property<File> getArgsFile()
}
Alternatively, it's also possible to extend the Named
interface:
interface ToolSetup extends Named {
Property<File> getArgsFile()
}