For what task.inputs is needed? Task itself has fi...
# community-support
k
For what task.inputs is needed? Task itself has fields that assigned with i.e. file() reference. How inputs vs fields are correlated?
t
task.inputs is almost never needed these days. There used to be cases where you'd want to configure free-form arguments with values where you'd want specific normalization or path sensitivity; you'd then declare those values explicitly in task.inputs and configure the free-form arguments at execution time (such that the task properties' values didn't contain those values at the time Gradle evaluates whether the task is up-to-date, but will use the configured task.inputs). These days, most such tasks have a
List<CommandLineArgumentProvider>
property to which you can add an object with properly-annotated properties, and Gradle will soon start to fail if the task is reconfigured at execution time. This could be (have been?) useful too to declare implicit task dependencies when wiring an output property to an input property, when the input property isn't a lazy
Property
, so similarly you'd declare the provider in task.inputs, and then get the provider value to configure the input property at execution time (this also works for wiring an extension property to a task input property). Nowadays, lazy properties and `CommandLineArgumentProvider`s makes this almost useless (but there are still cases where it's needed unfortunately) In other words, task.inputs allows declaring dynamic additional "properties" to a task.
One such example: passing a file name in a system property where you want to track the file's content to determine whether the task needs to run or is up-to-date. Before the argument providers, you'd have to do something like:
Copy code
tasks.named<TheTaskType>("theTask") {
  inputs.file(theFile).withPropertyName("the.property").withPathSensitivity(NONE).normalizeLineEndings()
  doFirst {
    systemProperty("the.property", theFile.absolutePath)
  }
}
Nowadays you could do:
Copy code
tasks.named<TheTaskType>("theTask") {
  forkOptions.jvmArgumentProviders.add(object : CommandLineArgumentProvider, Named {
    @Internal override fun getName() = "the.property"

    @InputFile @PathSensitive(NONE) @NormalizeLineEndings val file = theFile

    override fun asArguments() = listOf("-Dthe.property=${this.file.absolutePath}")
  })
}
v
I'd object to that actually. I would say, if you write a proper task class you define inputs via annotated fields and do not use
inputs...
. But if you write an ad-hoc task in the build script, you use
inputs...
to define the inputs of the task. In both cases, you can use
inputs...
to for example get the summary of input files for a task when you need them and not per input property for whatever reason. As a quick example, this is what I gave someone else a few minutes ago:
Copy code
val foo by tasks.registering {
    val output = layout.buildDirectory.file("foo.txt")
    outputs.file(output).withPropertyName("output")
    doLast {
        output.get().asFile.writeText("bar")
    }
}
val signFoo by tasks.registering {
    inputs.files(foo).withPropertyName("input")
    outputs.file(
        foo.map {
            val fooOutput = it.outputs.files.singleFile
            fooOutput.resolveSibling("${fooOutput.name}.asc")
        }
    ).withPropertyName("output")
    doLast {
        signing.sign(*inputs.files.files.toTypedArray())
    }
}
👀 1
t
I agree, though this is not incompatible with what I said 😉: adhoc tasks (without using any specific task type, as in your examples) are rather rare (I can't remember the last time I've needed this), and sure that's way terser than declaring an adhoc type (with annotated fields) for the task (I'd personally probably still go with a task type as it more clearly separates configuration from behavior and feels "cleaner" to me; just personal opinion though).
k
I thought code requires task name and task type as must have
v
adhoc tasks are rather rare
Not really. In fully idiomatic builds maybe where any imperative logic is moved to proper custom task classes and plugins, yes. But in reality out there, the need is quite often there. And sometimes you even need to use
inputs...
to add inputs that are not considered by task implementations. For example if you
expand
stuff in a
Sync
task like
processResources
, the values are not automatically inputs but have to be declared additionally.
just personal opinion though)
I fully agree. Unfortunately, reality often is different from what we wished everyone does.
I thought code requires task name and task type as must have
Not sure what you mean. If you mean my code, both is present. First task has type
DefaultTask
and name
foo
, second task has type
DefaultTask
and name
signFoo
.