Possible bug passing/defining command line paramters in one task breaks other tasks

I have a gradle script that takes 1 parameter from the command line:

apply plugin: 'java'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
  mavenCentral()
}
dependencies {
  testCompile group: 'junit', name: 'junit', version: '4.12'
}
task run(type: JavaExec) {
  main = 'NonAttackingQueens'
  //if (! project.hasProperty('queens')) project.ext.'queens' = '8'
  args "${queens}" 
  classpath = sourceSets.main.runtimeClasspath
}
test {
    testLogging {
        events "passed", "skipped", "failed"
    }
}

if I do a clean, or build or test then I get an error because - obviously - the queens command line parameter is not defined. here is the error:

* What went wrong:
A problem occurred evaluating root project 'non-attacking-queens'.
> Could not get unknown property 'queens' for task ':run' of type org.gradle.api.tasks.JavaExec.

This to me was surprising! In the java code the command line arg is not required and in fact defaults to 8 if not given. But, in order to make the gradle build happy and work I needed to add the commented out line:

if (! project.hasProperty('queens')) project.ext.'queens' = '8'

Now, I would agree that you could make an argument for the param being required if the run task is invoked. This would make queens a required param for the run task. But for the other tasks this seems strange to me.

Would you kindly consider making an args as a default and giving it a value of “” if not provided? You can keep the current args behavior for legacy reasons, but it would be great if the command line params could default to a given value if not provided.

Breaking the other tasks because the run task has an unsatisfied param was a big surprise. I don’t know if others have experienced this as well.

I am using gradle 4.4.1.

Thanks for listening.

A Gradle build script is code (in this case Groovy) that needs to implement your requirements, same as application code. Just as it would be unreasonable to expect this code to compile in Java because the declaration of b is commented out (even if c is not used):

public void calculate() {
    int a = 1;
  //int b = 2;
    int c = a + b;
}

It is also unreasonable to try to access a property called queens for every build, even when you don’t expect this value to be specified. Writing "${queens}", an eagerly evaluated Groovy String, specifically requests for this to happen. The failure just happens later in the process.

This is completely tied to how you’re accessing an undefined property in configuration phase code. The fact that you’re using it as an argument for a JavaExec isn’t relevant. It fails before it even can try to use the value to call args on the task. Additionally, a property can be specified many ways. Specifying the property could be done without providing it on the command line via property files, environment, or additional coding in the file.

If you want queens to be a required property only when running the run task, you should specify a value that is only evaluated when it is used to execute the run task, not eagerly for every single build:

task run(type: JavaExec) {
    main = 'NonAttackingQueens'
    args "${->queens}" // Lazy Groovy String
    classpath = sourceSets.main.runtimeClasspath
}

However, it seems more likely that you want to model the actual behavior of the application. If the command line argument is optional for NonAttackingQueens, you shouldn’t add one at all in the run task unless the user is doing something to specify one.

task run(type: JavaExec) {
    main = 'NonAttackingQueens'
    if (project.hasProperty('queens')) {
        args "${queens}"
    }
    classpath = sourceSets.main.runtimeClasspath
}

This models the expectation that NonAttackingQueens is only called with an argument when the queens property is provided (by any method that can supply a Gradle property).

Thank-you very much. That resolved my issue and answered my question.