Vampire
02/28/2025, 11:55 AMProperty times I had an @Internal property that was set to either,
and then a derived @Optional @InputFile read-only property
and a derived @Optional @InputDirectory read-only property
which checked whether the internal property is a file or directory
and at execution time checked that exactly one of them is set.
I'm thinking about cleaner ways to rewrite this, without splitting the task into multiple tasks.ephemient
02/28/2025, 12:31 PM@get:Nested var input: Either
interface Either {
abstract class File : Either {
@get:InputFile abstract val file: RegularFileProperty
}
abstract class Directory : Either {
@get:InputDirectory abstract val dir: DirectoryProperty
}
}Martin
02/28/2025, 2:12 PMVampire
02/28/2025, 2:30 PMConfigurableFileCollection is most probably what I want here.
When I originally wrote that construct I was on Gradle 1.0 and not really knowing what I do. 😄
But besides that, @ephemients idea with some adjustments also works like intended.
For completeness sake, this is what I now played with first and works like before:
abstract class Foo : DefaultTask() {
@get:Inject
abstract val layout: ProjectLayout
@get:Inject
abstract val providers: ProviderFactory
@get:Internal
abstract val input: Property<FileSystemLocation>
@get:Optional
@get:InputFile
val inputFile: Provider<RegularFile> = input.map {
it.asFile.takeIf { it.isFile }?.let { layout.file(providers.provider { it }).get() }
}
@get:Optional
@get:InputDirectory
val inputDirectory: Provider<Directory> = input.map {
it.asFile.takeIf { it.isDirectory }?.let { layout.dir(providers.provider { it }).get() }
}
@TaskAction
fun execute() {
println(
when {
!input.isPresent -> "Input is not configured"
!input.get().asFile.exists() -> "Input does not exist"
inputFile.isPresent -> "Input file: ${inputFile.get().asFile.absolutePath}"
inputDirectory.isPresent -> "Input directory: ${inputDirectory.get().asFile.absolutePath}"
else -> "Input has unexpected type"
}
)
}
}
val foo by tasks.registering(Foo::class) {
outputs.upToDateWhen { true }
input = bar.flatMap { it.outputFile }
// input = layout.buildDirectory.file("non-existing")
// input = baz.flatMap { it.outputDirectory }
// input = layout.buildDirectory.dir("non-existing")
}
and this with the idea of @ephemient:
abstract class EitherFactory {
@get:Inject
abstract val objects: ObjectFactory
fun file(block: Either.File.() -> Unit) = objects.newInstance<Either.File>().apply(block)
fun dir(block: Either.Directory.() -> Unit) = objects.newInstance<Either.Directory>().apply(block)
}
extensions.create<EitherFactory>("eitherFactory")
sealed interface Either {
abstract class File : Either {
@get:InputFile
abstract val file: RegularFileProperty
}
abstract class Directory : Either {
@get:InputDirectory
abstract val dir: DirectoryProperty
}
}
abstract class Foo : DefaultTask() {
@get:Inject
abstract val layout: ProjectLayout
@get:Inject
abstract val providers: ProviderFactory
@get:Nested
abstract val input: Property<Either>
@TaskAction
fun execute() {
println(
when (input.get()) {
is Either.File -> "Input file: ${(input.get() as Either.File).file.get().asFile.absolutePath}"
is Either.Directory -> "Input directory: ${(input.get() as Either.Directory).dir.get().asFile.absolutePath}"
}
)
}
}
val foo by tasks.registering(Foo::class) {
outputs.upToDateWhen { true }
input = eitherFactory.file { file = bar.flatMap { it.outputFile } }
// input = eitherFactory.file { file = layout.buildDirectory.file("non-existing") }
// input = eitherFactory.dir { dir = baz.flatMap { it.outputDirectory } }
// input = eitherFactory.dir { dir = layout.buildDirectory.dir("non-existing") }
}ephemient
02/28/2025, 3:25 PMwhen (val either = input.get()) {
is Either.File -> "Input file: ${either.file.get().asFile.absolutePath}"
is Either.Directory -> "Input directory: ${either.dir.get().asFile.absolutePath}"
}
instead of casting a second getephemient
02/28/2025, 3:27 PMFileCollection