Whenever I try to run my build I get the following kind of error for all of the custom task’ properties:
* What went wrong:
Some problems were found with the configuration of task ':app:createSecondaryLauncher' (type 'CreateLauncherPropertiesTask').
- In plugin 'lmc.java-conventions' type 'com.zcode.lmc.build.CreateLauncherPropertiesTask' property 'appVersion' doesn't have a configured value.
Reason: This property isn't marked as optional and no value has been configured.
Possible solutions:
1. Assign a value to 'appVersion'.
2. Mark property 'appVersion' as optional.
For more information, please refer to https://docs.gradle.org/8.10.2/userguide/validation_problems.html#value_not_set in the Gradle documentation.
Could someone please help me understand what exactly is wrong here? Why is gradle complaining that these properties don’t have configured values for the java conventions plugin, even though this task is registered and configured in the application convention plugin?
Your .convention(null) calls are actually no-ops and could be removed.
No convention is the default.
So as the error message says, your input properties do not have a configured value.
Using convention(null) means “no convention value, so unset if convention is used”.
Using set(null) means “use the convention if set or otherwise unset”.
Using set(providers.provider { null }) means “unset even if a convention is configured”.
As the error also tells, only input properties marked with @Optional are allowed to nave no value configured.
Without @Optional you have to configure some value when using that task type.
How to properly resolve the situation is hard to say as you did not tell what those input properties are, respectively how they are used and whether it is ok to have no value set or whether a value always have to be set.
Your .convention(null) calls are actually no-ops and could be removed.
No convention is the default.
This is the full content of CreateLauncherPropertiesTask.kt:
CreateLauncherPropertiesTask.kt
package com.zcode.lmc.build
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
abstract class CreateLauncherPropertiesTask : DefaultTask() {
companion object {
const val SPACE = " "
const val MAIN_JAR_PROPERTY = "main-jar"
const val MAIN_CLASS_PROPERTY = "main-class"
const val ARGUMENTS_PROPERTY = "arguments"
const val JAVA_OPTIONS_PROPERTY = "java-options"
const val APP_VERSION_PROPERTY = "app-version"
const val ICON_PROPERTY = "icon"
const val WIN_CONSOLE = "win-console"
}
private val contentBuilder = StringBuilder()
@get:Input
abstract val mainJar: Property<String>
@get:Input
abstract val mainClass: Property<String>
@get:Input
abstract val arguments: ListProperty<String>
@get:Input
abstract val javaOptions: ListProperty<String>
@get:Input
abstract val appVersion: Property<String>
@get:InputFile
abstract val icon: RegularFileProperty
@get:Input
abstract val winConsole: Property<Boolean>
@get:OutputFile
abstract val propertiesFile: RegularFileProperty
@TaskAction
fun action() {
val outputFile = propertiesFile.get().asFile
if (!outputFile.createNewFile()) {
throw GradleException("Could not create launcher properties file: ${outputFile.name}")
}
if (mainJar.isPresent) {
appendProperty(MAIN_JAR_PROPERTY, mainJar.get())
if (mainClass.isPresent) {
appendProperty(MAIN_CLASS_PROPERTY, mainClass.get())
}
}
if (arguments.isPresent && arguments.get().isNotEmpty()) {
appendProperty(ARGUMENTS_PROPERTY, joinList(arguments.get()))
}
if (javaOptions.isPresent && javaOptions.get().isNotEmpty()) {
appendProperty(JAVA_OPTIONS_PROPERTY, joinList(javaOptions.get()))
}
if (appVersion.isPresent) {
appendProperty(APP_VERSION_PROPERTY, appVersion.get())
}
if (icon.isPresent) {
appendProperty(ICON_PROPERTY, icon.get().asFile.absolutePath)
}
if (winConsole.isPresent) {
appendProperty(WIN_CONSOLE)
}
}
private fun appendProperty(name: String) {
contentBuilder.appendLine(name)
}
private fun appendProperty(name: String, value: String) {
contentBuilder.appendLine("$name=$value")
}
private fun joinList(list: List<String>): String {
return list.joinToString(separator = SPACE)
}
}
The purpose of this task is to create a .properties file that will be used to add another launcher via the --add-launcher option of jpackage.
As mentioned in my previous post, this task is mainly used in the main application build script, which contains the following:
Would you recommend taking a different approach? Another point of interest is why is Gradle complaining about those properties for the lmc.java-conventions.gradle.kts plugin, although the task itself is only used in the lmc.application-conventions.gradle.kts plugin and in the application build script?
I did not analyze your task in detail, but as you check all those input properties with isPresent, they are actually optional and it is expected and appropriate if they are not set, so add @get:Optional to those input properties and remove the convention(null) calls and you should have the result you intended.
Another point of interest is why is Gradle complaining about those properties for the lmc.java-conventions.gradle.kts plugin, although the task itself is only used in the lmc.application-conventions.gradle.kts plugin and in the application build script?
I’m not sure, but I guess Gradle just searches in which plugin artifact the task type CreateLauncherPropertiesTask is in. As you have both of the plugins in one project, the task type is brought in by both plugins. I guess Gradle just takes the first found plugin for that output and does not check which plugin was actually used to bring the task class to the classpath.