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?
- Test project contains a unit test written for Gradle TestKit which tries to run a similar build script.
CodePudding user response:
Register an extension
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())
Non-extension method
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)
Avoiding the import
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).