Task.outputs deadlock - how to declare lazy outputs based on lazy inputs?

I need my task (finalizerTask) to resolve some files from a configuration and copy them into a folder that is already the output of another task (sourceTask), which is finalized by my task.

One option is to do so:

tasks.register("finalizerTask") {
    val sourceTask = tasks["sourceTask"] as SourceTask
    outputs.dir(sourceTask.outputDirectory) // a Property<Directory>
    // ...
}

However apparently this is not allowed because I get this error:

Could not determine the dependencies of task ‘:sourceTask’.
Could not create task ‘:finalizerTask’.
Task ‘:sourceTask’ property ‘outputDirectory’ is already declared as an output property of task ‘:sourceTask’ (type SourceTask). Cannot also declare it as an output property of task ‘:finalizerTask’ (type DefaultTask).

This can be fixed by resolving the Property<Directory> using get() so that the dependency is lost, however I don’t know if I can be sure that this property will be available by the time this task is configured.

Another option which I would like to implement is to declare as outputs the exact files I will be copying over. To do this I need to know the finalizer input files, which come from a configuration. One of the things I have tried is:

val lazyInputCollection: FileCollection = configuration.incoming.artifactView {
    // query the configuration
}.files
inputs.files(lazyInputCollection)
val lazyOutputCollection: Provider<FileCollection> = lazyInputCollection.elements.map { 
    // compute output files based on input files
    // return a new collection with them
}
outputs.files(lazyOutputCollection)

This only works when the build folder is cleared. If I call it more than once, the build hangs with lots of debug logs from org.gradle.cache.internal.DefaultFileLockManager.

2020-08-01T13:02:29.176+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Waiting to acquire shared lock on daemon addresses registry.
2020-08-01T13:02:29.176+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Lock acquired on daemon addresses registry.
2020-08-01T13:02:29.176+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Releasing lock on daemon addresses registry.
2020-08-01T13:02:29.177+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Waiting to acquire shared lock on daemon addresses registry.
2020-08-01T13:02:29.177+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Lock acquired on daemon addresses registry.
2020-08-01T13:02:29.177+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Releasing lock on daemon addresses registry.
2020-08-01T13:02:39.174+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Waiting to acquire shared lock on daemon addresses registry.
2020-08-01T13:02:39.174+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Lock acquired on daemon addresses registry.
2020-08-01T13:02:39.174+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Releasing lock on daemon addresses registry.
2020-08-01T13:02:39.174+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Waiting to acquire shared lock on daemon addresses registry.
2020-08-01T13:02:39.175+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Lock acquired on daemon addresses registry.
2020-08-01T13:02:39.175+0200 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Releasing lock on daemon addresses registry.

Another option is to resolve the configuration during the task configuration, but I would prefer if this was done at execution time instead. Is there’s any solution to this problem of declaring lazy outputs from lazy inputs, or are we forced to resolve the inputs?