Hi people, I'm trying to create a convention plugi...
# plugin-development
h
Hi people, I'm trying to create a convention plugin aka custom gradle plugin, which can be reused by any repository/project in my company. Unfortunately, The properties don't get updated/refreshed. I hope someone can help me. the example will follow as text snippets https://gradle-community.slack.com/files/U06LZ4XJR6U/F06L4CSLDML/dvconfigextension.kt https://gradle-community.slack.com/files/U06LZ4XJR6U/F06LAV47YAX/kotlinbaseplugin.kt
Copy code
dvConfig {
    publishable.set(true)
}
Setting the config in the gradle file, doesn't have any effect
v
You immediately request the values of the extension properties without giving the consumer build the chance to set the configuration by calling
get()
on the properties. You might be tempted to "fix" that by using
afterEvaluate { ... }
but don't, it is evil and should be avoided at almost any cost. Especially to set properties from other properties calling
.get()
is pretty counterproductive, as with that you void the sense in using the lazy properties. Instead you should wire your extension properties to the properties that you want to set, this way they are resolved as late as possible and thus hopefully after the user configured the extension, unless there is something else causing eager evaluation.
For example instead of
Copy code
toolchain.languageVersion.set(JavaLanguageVersion.of(extension.jvmTarget.get()))
you would do
Copy code
toolchain.languageVersion.set(extension.jvmTarget.map(JavaLanguageVersion::of))
h
Thanks @Vampire, that makes sense. any recommendation how I can handle
publishable
? because this one will be used in another plugin do add additional configs
Copy code
public abstract class KotlinLibraryPlugin : Plugin<Project> {
    public override fun apply(project: Project): Unit =
        with(project) {
            plugins.apply(KotlinBasePlugin::class.java)
            plugins.apply(JavaLibraryPlugin::class.java)

            val extension = extensions.getByType(DvConfigExtension::class.java)

            logger.warn("library known extension for publishable = ${extension.publishable.get()}")

            extensions.getByType(JavaPluginExtension::class.java).apply {
                withJavadocJar()
                withSourcesJar()
            }

            if (extension.publishable.get()) {
                addKotlinCompilerOptions()
            }
        }

    private fun Project.addKotlinCompilerOptions() {
        extensions.getByType(KotlinJvmProjectExtension::class.java).apply {
            compilerOptions.apply {
                freeCompilerArgs.set(
                    mutableListOf<String>().apply {
                        addAll(freeCompilerArgs.get())
                        add("-Xjsr305=strict")
                        add("-Xexplicit-api=strict")
                    }
                )
            }
        }
    }
}
v
Do not make it a property in the extension, but a function in the extension, then do the configuration in that function and the consumer can call that function to enable that configuration
h
do you probably have an example for that? as I just started this week with writing plugins
v
Not at hand, but feel free to ask further if you have a specific problem with that. 🙂
Basically just create a function
publishable()
in the extension and move the code that you do in
addKotlinCompilerOptions
inside that function.
The
Project
instance you can simply let Gradle inject into the extension as long as you let Gradle instantiate it and don't do it yourself which is always a good idea as Gradle also adds some decoration like making it
ExtensionAware
(which I personally also always declare explicitly, that makes using it later easier, you just need to declare that you extend
ExtensionAware
, nothing else necessary)
h
seems to work. I still hope, that one day we can have something that works as originally described by me.
v
Relatively unlikely. Properties are for evaluation as late as possible, not for reacting to it being set. Even if you could "react" to it being changed, what would you do if the it is set to
true
and then to
false
? And assuming there is some
doThisAfterTheConfigurationWasDone
, there would probably be little that prevents the consumer to also use that and change it again, making your plugin still reacting to the wrong value (actually despite the different name, that exactly is
afterEvaluate { ... }
and one of its biggest problems and why `Property`/`Provider` was added.
What you could do if you really want a property instead of method, is to model it like
Copy code
dvConfig {
    config {
        publishable.set(true)
    }
}
Here again
config
would be a function in your extension that gets an
Action<ClassWithPublishableField>
. In the
config
function you would start with ensuring it is only called once in a lifetime and after the check you would execute the argument and can safely react to the values.
Ah, one point missed, one could save a reference to the property and still modify it later, so after calling the argument, you would also call finalize value on all the properties, so that they cannot be modified any further. This might also already be sufficient as once-in-a-lifetime verification but with an explicit check you could provide a more meaningful error message.
So summarized, `Property`s are really meant to be wired to other properties, or evaluated at execution time and not for configuration time usage.
h
is there some other posibility to have something for the config time usage?
v
What do you not like with having
publishable()
function?
h
it just irritates me, but probably I have to get used to it.
👌 1