hi, I'm trying to follow the Gradle guide for writ...
# gradle
c
hi, I'm trying to follow the Gradle guide for writing a custom plugin as a standalone project as described here https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html#plugin-development-plugin code in thread
Copy code
abstract class SyncFigmaPluginExtension {
    abstract fun getAccessToken(): Property<String>
    abstract fun getIconsFileId(): Property<String>
}

class SyncFigmaPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        val extension = project.extensions.create("syncIcons", SyncFigmaPluginExtension::class.java)

        project.tasks.create("syncIcons", SyncFigmaIcons::class.java)
        project.afterEvaluate {
            project.tasks.getByName("syncIcons") {
                val task  = it as SyncFigmaIcons
                task.accessToken.set(extension.getAccessToken())
                task.iconsFileId.set(extension.getIconsFileId())
            }
        }
    }
}

abstract class SyncFigmaIcons : DefaultTask() {

    @get:Input
    abstract var accessToken: Property<String>

    @get:Input
    abstract var iconsFileId: Property<String>
this is my task and I've tried so many approaches but I always get the error that the extension is not found
I apply the plugin like
Copy code
id 'com.sitoo.figma.sync' apply false
because if I apply it, it will crash with
getAccessToken
returning null
in my build.gradle where plugin is applied, I want to do
Copy code
syncIcons {
    accessToken = "xxx"
    iconsFileId = "abc"
}
v
As a slight beautifying, you can make the extension an interface:
Copy code
interface SyncFigmaPluginExtension {
    fun getAccessToken(): Property<String>
    fun getIconsFileId(): Property<String>
}
Also, when using Kotlin you shouldn't use getter functions but properties, otherwise you will not be able to access them like you want from Kotlin DSL, so
Copy code
interface SyncFigmaPluginExtension {
    val accessToken: Property<String>
    val iconsFileId: Property<String>
}
Then, you should never use
afterEvaluate
whenever you can prevent it. The only thing you do in most situations is introducing timing problems and race conditions. And with using the lazy property APIs it is also totally non-sense, they are introduced to exactly eliminate the horrible
afterEvaluate
need. Besides that it makes your code super-clumsy, so
Copy code
project.tasks.create("syncIcons", SyncFigmaIcons::class.java) {
    accessToken.set(extension.accessToken)
    iconsFileId.set(extension.iconsFileId)
}
or
Copy code
project.tasks.create("syncIcons", SyncFigmaIcons::class.java) {
    accessToken.convention(extension.accessToken)
    iconsFileId.convention(extension.iconsFileId)
}
depending on the intended semantics. Next thing is, that you should use task configuration avoidance wherever you can to not do unnecessary work, so do not use
tasks.create
, but
tasks.register
, so
Copy code
project.tasks.register("syncIcons", SyncFigmaIcons::class.java) {
    accessToken.convention(extension.accessToken)
    iconsFileId.convention(extension.iconsFileId)
}
And if you use the
kotlin-dsl
plugin on the project building the plugin, you can also use the Kotlin DSL extensions of the Gradle API like
Copy code
project.tasks.register<SyncFigmaIcons>("syncIcons") {
    accessToken.convention(extension.accessToken)
    iconsFileId.convention(extension.iconsFileId)
}
Or if you want to avoid strings or need the reference to the task later anyway,
Copy code
val syncIcons by project.tasks.registering(SyncFigmaIcons::class) {
    accessToken.convention(extension.accessToken)
    iconsFileId.convention(extension.iconsFileId)
}
But those were all just improvement suggestions.
I apply the plugin like
Copy code
id 'com.sitoo.figma.sync' apply false
No, that does not apply the plugin, you tell it not to. You just add the plugin to the classpath, but do not apply it. Then the extension of course is also not available.
because if I apply it, it will crash with
getAccessToken
returning null
Maybe that was due to not using a property but a getter function, I'm not sure.
c
thank you for your elaborate response! it seems like declaring the extension as an interface did the trick!
I was not using
afterEvalute
at first, but found it while googling solutions
r
I am on a similar usecase where I need to create a plugin (for storing HEAD's git short sha in an asset and current build time) ; without
afterEvaluate
it doesn't work at all. With it, each time app build is triggered the plugin stores git-time data in a JSON (business need). How to not use afterEvaluate in this case ?
v
Just don't. What you said had no indication
afterEvaluate
would change anything. If it does, you need to provide more information.