If I have a plugin which defines a new project property:
// Common plugin
var copyrightDate: String? by project.extra
And then try to access this property in a build script:
plugins {
`lifecycle-base`
id("acme.common")
}
copyrightDate = "2022"
I get the predictable result of:
e: path\to\build.gradle.kts:7:1: Unresolved reference: copyrightDate
A workaround as noted in the answer here is to explicitly declare the property again each time you are about to use it, like:
var copyrightDate: String? by project.extra
Since we have a number of projects using the same plugin, I'd rather have this happen automatically, like how applying the java plugin makes a java {}
function available without having to declare it. This mechanism appears to be used by a number of plugins, including those not shipped with Gradle itself. But I'm yet to figure out how they're making it work.
How is this meant to be done?
The official way to do this is to register an extension.
First create an extension object. So long as it only has simple properties, it can be a Gradle managed type.
// src/main/kotlin/my/custom/plugin/MyCustomPluginSettings.kt
package my.custom.plugin
import org.gradle.api.provider.Property
interface MyCustomPluginSettings {
val copyrightDate: Property<String>
}
(I've used a Property<>
rather than a String
(the benefits are listed here) but you could also use a plain String
if you wanted.)
Next, in your plugin, create the extension, and set a default value for copyrightDate
.
// src/main/kotlin/my/custom/plugin/MyCustomPlugin.kt
package my.custom.plugin
import org.gradle.api.*
import org.gradle.kotlin.dsl.*
abstract class MyCustomPlugin : Plugin<Project> {
override fun apply(target: Project) {
val myCustomPluginSettings = target.extensions.create<MyCustomPluginSettings>("myCustomPlugin")
myCustomPluginSettings.copyrightDate.convention("2022")
}
}
Note that I'm using the Gradle Kotlin DSL. Make sure to apply the kotlin-dsl
plugin in your plugin's build.gradle.kts
!
You can also apply your MyCustomPluginSettings
in a buildSrc plugin in the same way - just use the contents of the apply(...) {}
function in the .kts
file.
Now when you apply your plugin, Gradle will automatically generate a Kotlin DSL accessor from the name you gave your extension.
// build.gradle.kts
plugins {
id("my.custom.plugin")
}
println(myCustomPlugin.copyrightDate.get())
If you define the property in a .kt
file, then so long as that file is included with the plugin.
(If you're writing buildSrc plugins, then the .kt
file can be anywhere in ./buildSrc/src/main/kotlin/...
)
// src/main/kotlin/my/custom/plugin/constants.kt
package my.custom.plugin
import org.gradle.api.Project
var Project.copyrightDate: String?
get() = extra["copyrightDate"] as String?
set(value) {
extra["copyrightDate"] = value
}
Now in a build.gradle.kts
uses can import this.
// build.gradle.kts
import my.custom.plugin.copyrightDate
plugins {
id("my.custom.plugin")
}
println(copyrightDate)
You can avoid the import by either putting constants.kt
in the source root, without a package, or in one of the default import packages, like org.gradle.kotlin.dsl
.
// src/main/kotlin/my/custom/plugin/constants.kt
package org.gradle.kotlin.dsl
import org.gradle.api.Project
var Project.copyrightDate: String?
get() = extra["copyrightDate"] as String?
set(value) {
extra["copyrightDate"] = value
}
Because of the risk of clashing, I only recommend this for buildSrc plugins. But this might be handy if you want to include a helper extension for something more complicated, like a helper function for defining dependencies with a default version (like how there's a dependencies { kotlin("reflect") }
helper function).