Jerome Haltom
07/17/2025, 2:04 PMMartin
07/17/2025, 2:06 PMmyExtension {
configureStuff { // registering task is happening here
myOption = "foobar"
}
}Jerome Haltom
07/17/2025, 2:07 PMJerome Haltom
07/17/2025, 2:07 PMMartin
07/17/2025, 2:08 PMJerome Haltom
07/17/2025, 2:08 PMJerome Haltom
07/17/2025, 2:08 PMMartin
07/17/2025, 2:09 PMopen class MyExtension {
fun configureStuff(action: Action<Options>) {
options = TODO()
action.execute(options)
// add your tasks here based on options
}
}Martin
07/17/2025, 2:09 PMthe user could technically call it multiple times.Right. You can add a runtime check but that's basically it
Martin
07/17/2025, 2:10 PMMartin
07/17/2025, 2:10 PMJerome Haltom
07/17/2025, 2:11 PMThe other option is to register all the tasks possible and only enable some of them based on user inputThat was my first approach, but it got ugly when I started having a long list of tasks that come and go, but need to have inputs connected ot outputs dynamically.
Martin
07/17/2025, 2:12 PMafterEvaluate {} and the likesThomas Broyer
07/17/2025, 2:13 PMJerome Haltom
07/17/2025, 2:14 PMJerome Haltom
07/17/2025, 2:15 PMJerome Haltom
07/17/2025, 2:15 PMVampire
07/17/2025, 2:16 PMwhenObjectAdded is eager, thus bad anyway.
And configureEach executes in order of registration.
So if your plugin registers a configureEach action and after that the user registers his task with a configure action, your plugin's action is executed first and then the user's action.
If the user is settings properties that you wire into your task, that will work.
But if you need the configured values at configuration time to decide what task to add or its name or similar, then it will not work.Vampire
07/17/2025, 2:17 PMThomas Broyer
07/17/2025, 2:30 PMI have an item called a Build, which I put in a NamedDomainObjectContainer on my extension. I set up configureEach on the container. But that seems to happen before the user calls to the items inside the container run.Then you're not fully reactive, the "user calls to the items inside the container" need to be imperative then.
Thomas Broyer
07/17/2025, 2:34 PMjava.withJavadocJar() and java.withSourcesJar())Jerome Haltom
07/17/2025, 2:37 PMcics {
builds {
named("aquOnline") {
maps {
remote = remotes.named("aix")
options {Jerome Haltom
07/17/2025, 2:37 PMJerome Haltom
07/17/2025, 2:38 PMJerome Haltom
07/17/2025, 2:38 PMJerome Haltom
07/17/2025, 2:39 PMJerome Haltom
07/17/2025, 2:40 PMJerome Haltom
07/17/2025, 2:41 PMVampire
07/17/2025, 2:41 PMconfigureEach action.
Then the build script is executed and registers its named... action.
So your plugin's action does not see the changes from the build script and there is no clean way to make it if you need the values at configuration time except for having a function instead that the user calls.Vampire
07/17/2025, 2:42 PMJerome Haltom
07/17/2025, 2:43 PMVampire
07/17/2025, 2:43 PMJerome Haltom
07/17/2025, 2:43 PMVampire
07/17/2025, 2:43 PMVampire
07/17/2025, 2:43 PMLike a big wrapping configure method.Exactly
Jerome Haltom
07/17/2025, 2:44 PMcics {
build {
named("foo") {
configure {
maps {}
otherthing {}Jerome Haltom
07/17/2025, 2:44 PMVampire
07/17/2025, 2:44 PMconfigure as that might conflict with Gradle built-in function, but conceptually, yes, exactly that.Jerome Haltom
07/17/2025, 2:49 PMJerome Haltom
07/17/2025, 2:49 PMVampire
07/17/2025, 2:51 PMJerome Haltom
07/17/2025, 2:52 PMVampire
07/17/2025, 2:52 PMafterEvaluate, the user could use afterEvaluate too and thus do configuration after your action.
One of the joys of using afterEvaluate and actually it's main effect, introducing ordering problems, timing problems, and race conditions.Vampire
07/17/2025, 2:53 PMMartin
07/17/2025, 2:53 PMafterEvaluate {} to save one nesting level and have an "easy" API. Took us several years to get rid of it.
The "easy" API was counterproductive because as soon as you needed some custom config, you needed to use the "explicit" API anyways. Go straight for the "explicit" API and save yourself a lot of trouble, not to mention consistent documentation, etc...Vampire
07/17/2025, 2:54 PMJerome Haltom
07/17/2025, 2:55 PMVampire
07/17/2025, 2:55 PMVampire
07/17/2025, 2:55 PMJerome Haltom
07/17/2025, 2:55 PMVampire
07/17/2025, 2:56 PMJerome Haltom
07/17/2025, 2:57 PMcics {
build {
named("foo") {
children {
named("bar") {
maps {}
otherthing {}Jerome Haltom
07/17/2025, 2:57 PMJerome Haltom
07/17/2025, 2:58 PMVampire
07/17/2025, 2:59 PMfoo and bar are both on build, and you realize bar but not foo, then you miss the configuration of bar as the configuration of foo never executed which would register the configuration action for bar.Jerome Haltom
07/17/2025, 3:00 PMVampire
07/17/2025, 3:00 PMconfigureEach for build in your plugin, then this would still run before that configure action, so the bar configure action would not be registered yet either.Vampire
07/17/2025, 3:01 PMcics {
build {
named("foo") {
maps {}
otherthing {}
doTheTaskCreationNow()
But then the user could forget to call that function and you also have to make sure he does not do additional configuration changes after that function was called.Vampire
07/17/2025, 3:02 PMJerome Haltom
07/17/2025, 3:03 PMVampire
07/17/2025, 3:04 PMcics {
build {
named("foo") {
var bar
configure {
maps {}
bar = maps
otherthing {}
}
// do something bad with bar
but you do not try make it evil-user-proof, just convenient and reliable and reproducable.Jerome Haltom
07/17/2025, 3:05 PMJerome Haltom
07/17/2025, 3:05 PMJerome Haltom
07/17/2025, 3:05 PMJerome Haltom
07/17/2025, 3:06 PMJerome Haltom
07/17/2025, 3:07 PMJerome Haltom
07/17/2025, 3:08 PMJerome Haltom
07/17/2025, 3:09 PMVampire
07/17/2025, 3:09 PMI have it all working cleanly using afterEvaluateThat is impossible, it just seems so. As I said, main benefit of
afterEvaluate is added ordering problems, timing problems, and race conditions.Thomas Broyer
07/17/2025, 3:10 PMVampire
07/17/2025, 3:11 PMJerome Haltom
07/17/2025, 3:12 PMJerome Haltom
07/17/2025, 3:13 PMJerome Haltom
07/17/2025, 3:14 PMephemient
07/17/2025, 3:14 PMonlyIf or @SkipWhenEmpty etc. to skip them if there's nothing to doVampire
07/17/2025, 3:15 PMI could also use the order of the method calls in the build() itself to define the chaining couldn't I.Probably
Vampire
07/17/2025, 3:15 PMephemient
07/17/2025, 3:15 PMJerome Haltom
07/17/2025, 3:16 PMcobol {
builds {
named("main") {
vcpre {}
vsqlpre {}
db2 {}
kixclt {}ephemient
07/17/2025, 3:16 PMJerome Haltom
07/17/2025, 3:16 PMJerome Haltom
07/17/2025, 3:16 PMJerome Haltom
07/17/2025, 3:16 PMJerome Haltom
07/17/2025, 3:17 PMJerome Haltom
07/17/2025, 3:22 PMJerome Haltom
07/17/2025, 9:18 PMJerome Haltom
07/17/2025, 9:20 PMJerome Haltom
07/17/2025, 9:23 PMVampire
07/17/2025, 9:32 PMTaskProvider<SpecificType> to a TaskProvider<Task>.
At runtime it is anyway just TaskProvider due to type-erasure.
But a TaskProvider<SpecificType> is a TaskProvider<? extends Task> so maybe you want to return that.
Or you have <T extends Task> as type parameter and return a TaskProvider<T>.
...Vampire
07/17/2025, 9:32 PMJerome Haltom
07/17/2025, 9:40 PMPhilip W
07/21/2025, 12:03 PMJerome Haltom
07/25/2025, 4:58 PMJerome Haltom
07/25/2025, 4:58 PMJerome Haltom
07/25/2025, 4:59 PMPhilip W
07/25/2025, 5:00 PMJerome Haltom
07/25/2025, 5:01 PMJerome Haltom
07/25/2025, 5:01 PMJerome Haltom
07/25/2025, 5:01 PMJerome Haltom
07/25/2025, 5:04 PMPhilip W
07/25/2025, 5:05 PM