Home > Back-end >  Getting unexpected `Cannot set the value of read-only property` when writing own plugin
Getting unexpected `Cannot set the value of read-only property` when writing own plugin

Time:01-15

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()
}
  • Related