I have a task that accepts a nested property of configuration parameters, which is then needed in the work action executed in parallel. However, when running the task it fails with
Execution failed for task ‘:1.21.1:stonecutterPrepare’.
A failure occurred while executing dev.kikugie.stonecutter.process.SCPrepareAction
Could not isolate value dev.kikugie.stonecutter.process.SCPrepareAction$Parameters_Decorated@285b7228 of type SCPrepareAction.Parameters
Could not serialize value of type StonecutterBuildParameters
So what do I need to make an object passed to the work action serializable/isolatable?
Relevant code:
public abstract class StonecutterBuildParameters @Inject internal constructor(flags: StonecutterFlags, current: Version, factory: ProviderFactory) {
@get:Input public abstract val constants: MapProperty<Identifier, Boolean>
@get:Input public abstract val swaps: MapProperty<Identifier, String>
@get:Input public abstract val dependencies: MapProperty<Identifier, Version>
@get:Nested public abstract val stringReplacements: ListProperty<StringReplacementSpec>
@get:Nested public abstract val regexReplacements: ListProperty<RegexReplacementSpec>
// Supplies 'dependencies' with a provider transforming this map
@get:Internal internal abstract val dummyDependencies: MapProperty<Identifier, Version>
@get:Inject protected abstract val objects: ObjectFactory
// Used to build string and regex replacements. Need to be properties to allow replacement graph validation.
@get:Internal private val stringReplacementBuilder: ReplacementBuilder<StringReplacement>
@get:Internal private val regexReplacementBuilder: ReplacementBuilder<RegexReplacement>
...
}
How do you get the error?
I’ve run all three testclasses where I found 1.21.1 and all were successful.
What is the full --stacktrace of the error?
Most probably it shows more information about what the actual problem is.
But I guess the problem are the two ReplacementBuilder properties, as they are no managed properties.
Btw. all the input annotations (@Input, @Nested, @Internal) should be pretty pointless on that class, unless you also use instances of that class as @Nested input on some task or artifact transform. Worker API actions are not individually up-to-dateable or cacheable afair.
From my understanding of the error, it needs every object to implement java.io.Serializable. If there’s no workaround, I can accept it being that. However it doesn’t make much sense because I run the worker without isolation, so doing something like this works (but I really don’t want to - it’s bodge of all bodges)
private val cache: MutableMap<Int, TransformParameters> = mutableMapOf()
public abstract class SCPrepareTask : DefaultTask() {
@get:Nested
public abstract val params: Property<StonecutterBuildParameters>
@TaskAction
public fun run(inputs: InputChanges) {
val data = params.get().build()
val key = Random.nextInt()
cache[key] = data
try {
executor.execute {
for (change in inputs.getFileChanges(source))
if (change.fileType != FileType.DIRECTORY) it.processFile(change, key)
}
} finally {
cache.remove(key)
}
}
private fun WorkQueue.processFile(change: FileChange, cacheKey: Int): Unit = submit(SCPrepareAction::class) {
key.set(cacheKey)
source.set(change.file)
output.set(change.file.cacheFile())
}
}
@OptIn(StonecutterInternalAPI::class)
private interface SCPrepareAction : WorkAction<SCPrepareAction.Parameters> {
interface Parameters : WorkParameters {
val key: Property<Int>
val source: RegularFileProperty
val output: RegularFileProperty
}
override fun execute() {
val source: Path = parameters.source.asFile().toPath()
val output: Path = parameters.output.asFile().toPath()
val params = cache[parameters.key.get()]!! // OK...
}
}
Thank you for your response, though in documentation it only recommends using Property<T>-s, which may be worth noting.
data class ExampleClass(val str: String)
abstract class ExampleTask : DefaultTask() {
// Is a Property-like getter, works as a task input with caching support
abstract val example: Property<ExampleClass>
}
interface ExampleParameters : WorkParameters {
// Is a Property-like getter, but is not supported
val example: Property<ExampleClass>
}
data class ExampleClass(val str: String) : java.io.Serializable
interface ExampleParameters : WorkParameters {
// Works fine when it implements java.io.Serializable
val example: Property<ExampleClass>
}
Yeah, it is a bit blurry there.
But as I said, it most probably has to either be Serializable or fully-managed.
“fully-managed” means the types of the Propertys are also managed types, which includes primitives, and strings, and other fully managed types.