What is the correct way to take a path as task inp...
# community-support
i
What is the correct way to take a path as task input? I often have tasks that create a specific file. That file is therefore an output of the task, but the user should be able to configure where it is placed. If I use
Copy code
@get:InputFile
abstract val file: RegularFileProperty
then Gradle tracks its state, which doesn't make sense since the file doesn't exist yet. I've seen
Copy code
@get:Input
abstract val file: Property<String>
used, which does work, but that's not nice to use when configuring a task.
The old task was
Copy code
val createVersion by tasks.registering {
	description = "Store the self version into the resources"

	val file = project.layout.projectDirectory.file("src/main/kotlin/Self.kt").asFile

	doLast {
		file.writeText("""
			package dev.opensavvy.conventions.versions
			
			const val OPENSAVVY_CONVENTIONS_VERSION = "$version"
		""".trimIndent())
	}

	inputs.property("version", version)
	outputs.file(file)
}
and the best way I've found to rewrite it is
Copy code
val createVersion by tasks.registering(EmbedVersionTask::class) {
	sources.set(project.layout.projectDirectory.file("src/").toString())
	this.version = project.version.toString()
}

abstract class EmbedVersionTask : DefaultTask() {

	@get:Input
	abstract val sources: Property<String>

	@get:Internal
	val writePath: Provider<String>
		get() = sources.map { "$it/main/kotlin/Self.kt" }

	@get:Input
	abstract val version: Property<String>

	init {
		description = "Store the self version into the resources"

		outputs.file(writePath)
	}

	@TaskAction
	fun embedVersion() {
		File(writePath.get()).writeText("""
			package dev.opensavvy.conventions.versions
			
			const val OPENSAVVY_CONVENTIONS_VERSION = "${version.get()}"
		""".trimIndent())
	}
}
but there must be a better way, right?
p
Using the path name as string input is common, but in this case I absolutely don’t think you should configure the output file at all, because your task generates a file in the regular sources, so a user could change the file accidentally. IMHO, you should just generate the file in the build folder and wire the output directory to the sources folder
πŸ‘ 1
βž• 1
πŸ’― 1
πŸ‘† 1
t
Anyway, this should be an
@OutputFile
(just like you configured
outputs.file()
in the original code)
πŸ‘† 1
πŸ™ 1
Copy code
@get:OutputFile
abstract val file: RegularFileProperty
then something like
Copy code
val createVersion by tasks.registering(EmbedVersionTask::class) {
	this.version = project.provider { project.version.toString() }
    this.file = project.layout.projectDirectory.file("src/main/kotlin/Self.kt")
}
(I agree if this is automatically run during a build, it should output to the build directory; it's only acceptable to output to source trees if you run the task on its own and commit the generated file)
☝️ 2
πŸ‘† 1
m
the user should be able to configure where it is placed.
I stopped giving users the options to configure output locations. This is an anti pattern IMO as a misconfiguration could lead to overlapping outputs. +1 to @Philip W and @Thomas Broyer suggestion to output in the
build
directory
i
Thanks everyone!
πŸ‘ 1
m
Oh yes, it can (and should) be configurable as part of your task. Just I wouldn't expose that configuration to users of your plugin.
πŸ‘ 1