Is there a way to parameterize precompiled script ...
# gradle
n
Is there a way to parameterize precompiled script plugins? Can I somehow define configuration options in them?
s
For a low-tech approach, I sometimes have the project set an
ext
property and then have the script plugin read it in an
afterEvaluate
block. It’s not nice, though. If dynamic behaviour is needed, I would make a “real” plugin.
j
you can add extensions to them in the same way you can for normal plugins
v
A precompiled plugin is a real plugin, everything you can do in it you can also do in a precompiled script plugin.
Precompiled script plugins are just syntactic sugar
Whenever you use
ext
or
extra
is is more a dirty work-around and you should feel dirty. 😄
And
afterEvaluate
is practically always evil and should be avoided as hell. It usually just introduces timing problems, ordering problems, and race conditions.
s
🤯 I did not know this. I knew my approach was a horrible hacky workaround, but had no idea I could add extensions in a script plugin!
til thank you color
v
As Javier said, register an extension with properties and wire them to other properties. And if you need to "do something" based on the configuration the proper way often is to have a function in the extension in which you do the action.
n
@Vampire, thanks, maybe do you have an example as well? 😉
v
Of what? That is an extremely broad topic in general. Besides that, the documentation and sample projects should have quite some examples. If you mean about registering an extension, you basically create an interface with the properties you want to have and let Gradle implement and instantiate it using
extensions.create...
.
s
I’ve been playing around with this, and come up with something that works, but I’m not sure if it’s the “right” approach. In `my.plugin.gradle.kts`:
Copy code
interface MyExtension {
    val myVersion: Property<String>
}

val myExtension = extensions.create<MyExtension>("myExtension")

dependencies {
    implementation(myExtension.myVersion.orElse("3.2.1").map { "com.example:something:$it" })
}
and in `build.gradle.kts`:
Copy code
plugins {
    my.plugin
}

myExtension {
    myVersion.set("1.2.3")
}
It works, because the dependency provider isn’t evaluated until after the extension has been configured. But it feels a little fragile, and I’m wondering if there’s a better way for the plugin to “listen” for changes in the property 🤔.
v
No, you cannot "listen" for changes in a property. That's why you "wire" properties together and evaluate them as late as possible, optimally during execution phase. If you want to "listen" to the consumer doing a change, then you usually use a function in the extension instead that the consumer calls. If you want a "listenable" property, you would need to implement it yourself if that even is possible, never tried that.
s
So is my approach of using an extension property chained with
map
a “correct” approach?
j
I still miss a phase which allows applying plugins conditionally and still getting accessors.
v
I'm not 100% sure about this concrete use-case, but generally, yes. There might be better options for dependencies, but that might also depend on the concrete exact details.
Istill miss a phase which allows applying plugins conditionally and still getting accessors.
And you probably will forever. Generated accessor means, what you want to access is guaranteed to be there. That's the point of the type-safe generated accessors.
j
an special block could do the trick
but I don't sure if it is worth to file a request, I doubt they want to add that
but IMO, a convention plugin which is based in a DSL to select which features you want to implement, is better in large projects vs multiple conventions plugin which are not too flexible
Copy code
convention {
    plugins {
         alias(libs.plugins.hubdle)
    }

    hubdle {
        kotlin {
            multiplatform {
               features {
                   coroutines()
                   serialization()
                   sqldelight()
               }
            }
        }
    }
}

plugins {
  // likely empty always
}

// generated accessors for KMP, Serialization and Sqldelight extensions
the convention plugin in the convention dsl should recollect the state and all plugins are applied in the convention phase, but the rest of the state like adding dependencies and so on, is applied at start in the next the phase after the convention phase
v
I don't think that is really sanely doable. The
plugins { ... }
block that is extracted and applied to a dummy project is intentionally very restricted on what you can do there, that this works properly. Such a special block you describe could practically do any turing-complete logic that might then fail when extracted and run separately and so on. I still don't think anything like that will be added, but feel free to open a feature request if you think it is different to for example https://github.com/gradle/gradle/issues/16107 which was refused already.
j
if that was rejected, I doubt mine would be picked
320 Views