Pass command line argument to custom task

I am developing a plugin that contains a single custom task. The task looks for a command-line argument, say bar, and uses it to construct an input file name. However, it appears that the task @TaskAction method (and possibly the @InputFile-annotated fields?) is running before the plugin’s apply method. This leads to the bar property being null when the task tries to construct the input file name. What is the correct way to achieve my goal?

Example code and usage follows.

class FooPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        project.tasks.register("foo", FooTask) {
            if (project.hasProperty("bar") {
                def targetFileName = "my${bar}.txt"
            }
        }
    }
}
abstract class FooTask extends DefaultTask {
    @Input
    String bar

    @InputFile
    abstract RegularFileProperty getBarFile()

    @Option(option = "bar")
    void setBar(bar) {
        this.bar = bar
    }

    @TaskAction
    void doFoo() {
        // task logic using the bar variable
    }
}

How I’m attempting to run the task:
./gradlew foo -Pbar=baz

-P sets the value of a Gradle/project property, not a task option. You want to follow the task name with --<option> <val>. For example, ./gradlew foo --bar baz (--bar=baz also works).

You also need to provide a description value with @Option. For example, @Option(option="bar", description="this is the bar"). This description will appear in the task help, ./gradlew help --task foo.

Here’s how I might approach what you are trying to do

abstract class FooTask extends DefaultTask {
    @InputFile
    abstract RegularFileProperty getBarFile()
    @Option(option = "bar", description="blah")
    abstract Property<String> getBar()
    @TaskAction
    void doFoo() {
        println barFile.get().asFile
    }
}
class FooPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        project.tasks.register("foo", FooTask) {
            barFile.convention(bar.map { barVal ->
                project.layout.projectDirectory.file("my${barVal}.txt")
            })
        }
    }
}

Ok, good to know about how to pass command-line arguments to tasks, thank you. Also, I did have a description, but thought it was optional so left it out of my example.

I do have some questions about your code. Gradle says “Cannot have abstract method FooTask.getBar()”. Should that be a setter?

Second, my real code has two input files (one that has a hard-coded name and the other that’s constructed from the command-line argument) and outputs to a third file. I tried putting this all into the bar.map closure, but got a compiler warning:

‘convention’ in ‘org.gradle.api.file.RegularFileProperty’ cannot be applied to ‘(org.gradle.api.provider.Provider<java.lang.Object>)’