Alas: 1. How do I wire lazily set values from my ...
# plugin-development
l
Alas: 1. How do I wire lazily set values from my extension to function calls within another extension (the JavaPluginExtension, in this case) when convention values have been given in the Property definitions?
v
Also here, you imho better don't but have a function in your extension.
l
Like ..
Copy code
/**
   * Convenience method to set the [JavaVersion] to use within this project.
   *
   * @param javaVersion The Java version to use, both for source and target compatibility.
   */
  fun useJavaVersion(javaVersion: JavaVersion) = withJavaVersion.set(javaVersion)
Means that the DSL becomes
Copy code
ourJavaStandards {
  useJavaVersion(JavaVersion.VERSION_21)
}
... which is readable, typesafe and works well, I think.
v
fun useJavaVersion(javaVersion: JavaVersion) = withJavaVersion.set(javaVersion)
How would that "wire to a function call within JavaPluginExtension"? Just to set a property in your extension, I would not use a function. Unless of course you like the resulting DSL more, then it would be fine, but still not solve the question, would it?
l
It sets a property for use within my plugin. That plugin would then read the configured value and use it to set the corresponding property within the Kotlin and Java (and others) plugins to harmonize the configuration to one single place.
So the reason I am asking is that my extension is lazily configured with convention values. The other extensions are sometimes not. Currently, the best way I have found was to use the
afterEvaluate
block to find a time when the property from my extension has been given a chance to be set by users in the build file .... and then call the corresponding non-lazy methods in some other plugins
Alas: 1. If both extensions use Lazy Properties, wire them lazily using the
map{ ... }
directive 2. If either extension does not use Lazy Properties, use the
afterEvaluate { ... }
block to call the functions in the target (non-lazy) extension
v
It sets a property for use within my plugin.
That plugin would then read the configured value and use it to set the corresponding property within the Kotlin and Java (and others) plugins to harmonize the configuration to one single place.
But where in your plugin? In the
apply
is too early, as the consumer would not have set the value yet,
afterEvaluate
is evil. So the recommendation was to do the necessary configuration in that method the consumer is calling. Using
afterEvaluate
, as described in your other thread, is just symptom treatment and the main thing it does is introducing ordering problems, timing problems, and race conditions, which is why using it is discouraged bad practice. Btw. that single place in this case should be the JVM toolchain, which should then be taken into account by all tasks and also by the Kotlin Gradle plugin. :-)
l
And - as We established - That Works only if the function is called, and not with convention values
v
Yes and no. As I said, you can for example set the
*Compatibility
defaults in your
apply
method and then that function can re-configure it as needed and thus you get the behavior you want. It heavily depends on the concrete case.
l
That is what i do presently. Will revamp - and explain why - so the functions in the extension directly sets the value in their respective travet plugin extensions. But That is a rather weird Pattern. 🤠
Travet —> Target
DYAC 😉
v
Well, it might feel a bit weird to some, but simply is necessary, because there is no "build script finished configuration let plugin do additional configuration" hook. Maybe once the "Declarative Gradle" lands and you can somehow enforce it, there is. 🤷‍♂️
l
I realize that. However, the intent from reading Gradle docs seems to be: 1. State/configuration should be implemented in build extension, and 2. Logic/algoritghms should be implemented in the Plugin. However, what you describe is something else - while it certainly will work is is less clear: 1. State/configuration and setters for other Build Extensions (if the properties in them are non-lazy) should be implemented in the Build Extension 2. Algorithms, plus initial reading of (convention) state from build extension properties should be implemented in the Plugin If the buildsystem core is going Reactive (firing events when something happens and permitting listeners/Plugins to React to them), most of this complexity would vanish… and other complexities would emerge.
v
I fully agree, that idiomatic way is to only have configuration / state in extensions and logic in the plugin. But, well, sometimes we need to be a bit pragmatic. 😄 There is just no really good and reliable way to do it otherwise. Actually if the source separation of having the logic in the plugin and only state in the extension, then do the logic in a function in the plugin, inject the plugin instance into the extension and then forward those calls on the extension to the functions in the plugin. This way you have the actual logic still in the plugin, but can nevertheless trigger it from the consumer by calling a function in the extension. 🙂