RegularPileProperty vs Property<File>

Hi. I’m migrating the old plugin gradle-pitest-plugin created in the times of Gradle 1.0 to the Provider/Property API.

One of the things detected by my regression tests is a fact that plugin compiled with Gradle 5.6 throws:

ClassNotFoundException: org.gradle.api.file.FileSystemLocationProperty
when executed in lower Gradle version.

It’s caused by RegularFileProperty (added in 4.3) having a super interface FileSystemLocationProperty (added in 5.6). As I don’t want to stick with older Gradle to build my plugin nor force all the users to use Gradle 5.6+ I plan to use in the meantime Property<File> (instead of RegularPileProperty).

Q. Are they any drawbacks from using Property<File> instead of RegularPileProperty, e.g. related to the file changes detection (and I should stay with File and conventionMapping for those file properties)?

Update. Similar question could be related to Property<FileCollection> or Property<SourceSet>.

Do you have an example of this not working? I don’t think that’s intentional.

Property<File> will be more difficult to use and will work against anything else that uses RegularFile.

WRT Property<FileCollection>. This never makes sense. A FileCollection is already Provider-like, so you should just use a ConfigurableFileCollection.

I can’t say if Property<SourceSet> makes sense.

It should be enough to compile the following two plugin classes with Gradle 5.6 and run it with something <5.6 (tested with 5.5.1 and 4.10.2).

class PitestPluginExtension {
    final RegularFileProperty reportDir
...

    PitestPluginExtension(Project project) {
        reportDir = of.fileProperty()
...
    }
class PitestPlugin implements Plugin<Project> {
...
    private void createExtensionAndSetDefaultValues() {
        extension.reportDir.set(new File("${project.reporting.baseDir.path}/pitest"))
...

...
Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin [id 'info.solidsoft.pitest']
	at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:163)
	at org.gradle.api.internal.plugins.DefaultPluginManager.apply(DefaultPluginManager.java:133)
	at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.applyType(DefaultObjectConfigurationAction.java:120)
	at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.access$200(DefaultObjectConfigurationAction.java:38)
	at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction$3.run(DefaultObjectConfigurationAction.java:86)
	at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.execute(DefaultObjectConfigurationAction.java:143)
	at org.gradle.api.internal.project.AbstractPluginAware.apply(AbstractPluginAware.java:46)
	at org.gradle.api.internal.project.ProjectScript.apply(ProjectScript.java:34)
	at build_aedmfuce96wniqcf52rt3gad4.run(/home/foobar/Code/gradle-pitest-plugin/build/nebulatest/info.solidsoft.gradle.pitest.functional.PitestPluginGradleVersionFunctionalSpec/should-run-mutation-analysis-with-Gradle-5-5-1/build.gradle:2)
	at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:90)
	... 130 more
Caused by: org.gradle.api.reflect.ObjectInstantiationException: Could not create an instance of type info.solidsoft.gradle.pitest.PitestPluginExtension.
	at org.gradle.internal.instantiation.DependencyInjectingInstantiator.newInstance(DependencyInjectingInstantiator.java:53)
	at org.gradle.internal.extensibility.DefaultConvention.instantiate(DefaultConvention.java:224)
	at org.gradle.internal.extensibility.DefaultConvention.create(DefaultConvention.java:124)
	at info.solidsoft.gradle.pitest.PitestPlugin.createExtensionAndSetDefaultValues(PitestPlugin.groovy:80)
	at info.solidsoft.gradle.pitest.PitestPlugin.apply_closure1(PitestPlugin.groovy:60)
	at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1$1.run(DefaultCollectionCallbackActionDecorator.java:100)
	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.reapply(DefaultUserCodeApplicationContext.java:58)
	at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1.run(DefaultCollectionCallbackActionDecorator.java:97)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction.execute(DefaultCollectionCallbackActionDecorator.java:94)
	at org.gradle.api.internal.DefaultMutationGuard$2.execute(DefaultMutationGuard.java:41)
	at org.gradle.api.internal.collections.CollectionFilter$1.execute(CollectionFilter.java:59)
	at org.gradle.api.internal.DefaultDomainObjectCollection.configureEach(DefaultDomainObjectCollection.java:182)
	at info.solidsoft.gradle.pitest.PitestPlugin.apply(PitestPlugin.groovy:58)
	at org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget.applyImperative(ImperativeOnlyPluginTarget.java:42)
	at org.gradle.api.internal.plugins.RuleBasedPluginTarget.applyImperative(RuleBasedPluginTarget.java:50)
	at org.gradle.api.internal.plugins.DefaultPluginManager.addPlugin(DefaultPluginManager.java:177)
	at org.gradle.api.internal.plugins.DefaultPluginManager.access$300(DefaultPluginManager.java:51)
	at org.gradle.api.internal.plugins.DefaultPluginManager$AddPluginBuildOperation.run(DefaultPluginManager.java:267)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.api.internal.plugins.DefaultPluginManager$2.execute(DefaultPluginManager.java:155)
	at org.gradle.api.internal.plugins.DefaultPluginManager$2.execute(DefaultPluginManager.java:152)
	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:48)
	at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:152)
	... 139 more
Caused by: org.gradle.internal.instantiation.ClassGenerationException: Could not generate a decorated class for class info.solidsoft.gradle.pitest.PitestPluginExtension.
	at org.gradle.internal.instantiation.AbstractClassGenerator.generateUnderLock(AbstractClassGenerator.java:214)
	at org.gradle.internal.instantiation.AbstractClassGenerator.generate(AbstractClassGenerator.java:132)
	at org.gradle.internal.instantiation.AsmBackedClassGenerator.generate(AsmBackedClassGenerator.java:105)
	at org.gradle.internal.instantiation.ParamsMatchingConstructorSelector$1.transform(ParamsMatchingConstructorSelector.java:51)
	at org.gradle.internal.instantiation.ParamsMatchingConstructorSelector$1.transform(ParamsMatchingConstructorSelector.java:48)
	at org.gradle.cache.internal.DefaultCrossBuildInMemoryCacheFactory$DefaultCrossBuildInMemoryCache.get(DefaultCrossBuildInMemoryCacheFactory.java:121)
	at org.gradle.internal.instantiation.ParamsMatchingConstructorSelector.forParams(ParamsMatchingConstructorSelector.java:48)
	at org.gradle.internal.instantiation.DependencyInjectingInstantiator.newInstance(DependencyInjectingInstantiator.java:45)
	... 173 more
Caused by: java.lang.NoClassDefFoundError: org/gradle/api/file/FileSystemLocationProperty
	at org.gradle.internal.reflect.ClassInspector.inspectClass(ClassInspector.java:69)
	at org.gradle.internal.reflect.ClassInspector.visitGraph(ClassInspector.java:55)
	at org.gradle.internal.reflect.ClassInspector.inspect(ClassInspector.java:35)
	at org.gradle.internal.instantiation.AbstractClassGenerator.inspectType(AbstractClassGenerator.java:239)
	at org.gradle.internal.instantiation.AbstractClassGenerator.generateUnderLock(AbstractClassGenerator.java:187)
	... 180 more
Caused by: java.lang.ClassNotFoundException: org.gradle.api.file.FileSystemLocationProperty
	... 185 more

Nevertheless, if you had trouble reproducing that problem I would be able to create a branch in my project with the failing Travis build.

Looking at code (Gradle 5.6.1):

 * @since 4.3
 */
@Incubating
public interface RegularFileProperty extends FileSystemLocationProperty<RegularFile> {

and:

 * @since 5.6
 */
@Incubating
public interface FileSystemLocationProperty<T extends FileSystemLocation> extends Property<T> {

could be the reason.

Hrm, I can’t reproduce it with my sample plugin:

  • An extension with a RegularFileProperty field and a getter for it, initialized in the constructor.
  • A task with the same
  • A plugin that wires the two together

I built with Gradle 5.6 and then tried it with 5.5.1

If you have a branch to look at, I’m happy to take a look.

It turned out to be more complicated. Initially I was not able to reproduce it. Only:

NoSuchMethodError: org.gradle.api.model.ObjectFactory.fileProperty()Lorg/gradle/api/file/RegularFileProperty;

with 4.10.2 which is expected as that method was added in 5.0.

However, after some time I found that case, but the final impact is smaller. The problem occurs only with:

@CompileStatic
class PitestPluginExtension {

    final RegularFileProperty reportDir

    PitestPluginExtension(Project project) {
        ObjectFactory of = project.objects
        reportDir = of.fileProperty()
    }

    void setReportDir(String reportDirAsString) {
        this.reportDir.set(new File(reportDirAsString))
    }
}

where @CompileStatic and the setReportDir() method are crucial.

Caused by: org.gradle.api.reflect.ObjectInstantiationException: Could not create an instance of type info.solidsoft.gradle.pitest.PitestPluginExtension.
	at org.gradle.internal.instantiation.DependencyInjectingInstantiator.newInstance(DependencyInjectingInstantiator.java:53)
	at org.gradle.internal.extensibility.DefaultConvention.instantiate(DefaultConvention.java:224)
	at org.gradle.internal.extensibility.DefaultConvention.create(DefaultConvention.java:124)
	at info.solidsoft.gradle.pitest.PitestPlugin.apply_closure1(PitestPlugin.groovy:28)
	at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1$1.run(DefaultCollectionCallbackActionDecorator.java:100)
	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.reapply(DefaultUserCodeApplicationContext.java:58)
	at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1.run(DefaultCollectionCallbackActionDecorator.java:97)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction.execute(DefaultCollectionCallbackActionDecorator.java:94)
	at org.gradle.api.internal.DefaultMutationGuard$2.execute(DefaultMutationGuard.java:41)
	at org.gradle.api.internal.collections.CollectionFilter$1.execute(CollectionFilter.java:59)
	at org.gradle.api.internal.DefaultDomainObjectCollection.configureEach(DefaultDomainObjectCollection.java:182)
	at info.solidsoft.gradle.pitest.PitestPlugin.apply(PitestPlugin.groovy:27)
	at org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget.applyImperative(ImperativeOnlyPluginTarget.java:42)
	at org.gradle.api.internal.plugins.RuleBasedPluginTarget.applyImperative(RuleBasedPluginTarget.java:50)
	at org.gradle.api.internal.plugins.DefaultPluginManager.addPlugin(DefaultPluginManager.java:177)
	at org.gradle.api.internal.plugins.DefaultPluginManager.access$300(DefaultPluginManager.java:51)
	at org.gradle.api.internal.plugins.DefaultPluginManager$AddPluginBuildOperation.run(DefaultPluginManager.java:267)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.api.internal.plugins.DefaultPluginManager$2.execute(DefaultPluginManager.java:155)
	at org.gradle.api.internal.plugins.DefaultPluginManager$2.execute(DefaultPluginManager.java:152)
	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:48)
	at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:152)
	... 139 more
Caused by: org.gradle.internal.instantiation.ClassGenerationException: Could not generate a decorated class for class info.solidsoft.gradle.pitest.PitestPluginExtension.
	at org.gradle.internal.instantiation.AbstractClassGenerator.generateUnderLock(AbstractClassGenerator.java:214)
	at org.gradle.internal.instantiation.AbstractClassGenerator.generate(AbstractClassGenerator.java:132)
	at org.gradle.internal.instantiation.AsmBackedClassGenerator.generate(AsmBackedClassGenerator.java:105)
	at org.gradle.internal.instantiation.ParamsMatchingConstructorSelector$1.transform(ParamsMatchingConstructorSelector.java:51)
	at org.gradle.internal.instantiation.ParamsMatchingConstructorSelector$1.transform(ParamsMatchingConstructorSelector.java:48)
	at org.gradle.cache.internal.DefaultCrossBuildInMemoryCacheFactory$DefaultCrossBuildInMemoryCache.get(DefaultCrossBuildInMemoryCacheFactory.java:121)
	at org.gradle.internal.instantiation.ParamsMatchingConstructorSelector.forParams(ParamsMatchingConstructorSelector.java:48)
	at org.gradle.internal.instantiation.DependencyInjectingInstantiator.newInstance(DependencyInjectingInstantiator.java:45)
	... 172 more
Caused by: java.lang.NoClassDefFoundError: org/gradle/api/file/FileSystemLocationProperty
	at org.gradle.internal.reflect.ClassInspector.inspectClass(ClassInspector.java:69)
	at org.gradle.internal.reflect.ClassInspector.visitGraph(ClassInspector.java:55)
	at org.gradle.internal.reflect.ClassInspector.inspect(ClassInspector.java:35)
	at org.gradle.internal.instantiation.AbstractClassGenerator.inspectType(AbstractClassGenerator.java:239)
	at org.gradle.internal.instantiation.AbstractClassGenerator.generateUnderLock(AbstractClassGenerator.java:187)
	... 179 more
Caused by: java.lang.ClassNotFoundException: org.gradle.api.file.FileSystemLocationProperty
	... 184 more

You can reproduce it with gw funcTest in a separate branch, where I removed all not required code. @CompileDynamic over the method is a sufficient workaround.

And one more related question. What should be used instead of Set<File> in the Property-based approach?

@sterling Similar case is with DirectoryProperty:

@CompileStatic
class PitestTask extends DefaultTask {

    @OutputDirectory
    private final DirectoryProperty dir1

    PitestTask() {
        this.dir1 = project.objects.directoryProperty()
    }

    @TaskAction
//    @CompileDynamic
    void doNothing() {
        dir1.set(null)
    }
}