I’d like to put this code at the top of my build.gradle
services {
app1 {
main = 'org.foo.Bar'
public = true
}
app2 {
main = 'org.baz.Woo'
}
}
and then let my plugin define tasks like
app1Build
app1PackageAfterBuild
app2Build
app2PackageAfterBuild
I have my ServicePlugin but can’t find a way to feed it through a gradle-style DSL. I want to accomplish the same thing that Gradle does for DependencyHandler but need a clue as to where to watch for the relevant source code. Any help?
Following your answer I used Project.container() and registered a callback on container.whenObejctAdded. Unfortunately that callback is called before the newly added object is configured against its configuration closure. How can I be called after the application of the configuration closure instead?
No, it wouldn’t work. I need to be called after the object has been added
and configured but before user script is evaluated because the callback
creates tasks that the user may customize without being forced to use
.afterEvaluate himself.
I already do this for most tasks, but the problematic one is special
because the configuration is needed to build the task name itself, which
cannot be deferred. As a (hopefully) workaround I’m adding this task on
Project.afterEvaluate, but I really dislike it because this task is not
available in the user script (unless the user itself registers on after
evaluation).
But it seems there’s no solution for this in the current Gradle
implementation. If only there were a callback firing on after object
created and configured… I looked for it but could not find a way to hook
into.
I don’t know what you mean by “created and configured”, can you elaborate? Generally DSL objects can be mutated at any point, so there is no point in time where they are “fully configured”.
None of the values in there are final, they could be changed later in the build script. The only thing you can safely rely on for task naming is the domain object’s name.
apply plugin: 'foo'
services {
foo {
id 'foo-with-hyphens-illegal-in-groovy-identifiers'
set 'someValue'
set somePredefinedVaue()
}
}
This creates the tasks:
fooThis (depends on the name)
fooThat (depends on the name, too)
foo-with-hyphens-illegal-in-groovy-identifiers (depends on a value set by the configuration callback)
I don’t like that the third task must be created in Project.onAfterEvaluate because the build script then must wait in turn. To me there is a bug in NamedDomainObjectContainer.whenObjectAdded because it fires before the configuration callback is applied, but I have not found a way to replace it, yet.
That value can change later, it is not safe to use for task naming:
services {
foo {
id 'foo-with-hyphens-illegal-in-groovy-identifiers'
set 'someValue'
set somePredefinedVaue()
}
}
services.foo.id = "your-task-now-has-the-wrong-name"
Can you provide a more concrete use case why the task needs to have this complicated name?
The use case provided is concrete. It is production code, and the compilcated task name serves to illustrate the point: the actual task name is shorter but contains hyphens, that are not valid in Groovy identifiers and as such need to be quoted in build scripts.
Note that I’m not writing the build from scratch: it’s legacy code already integrated with many (I can’t even count) tools, so the more stable the build interface stays, the better. Anyway I already found a workaround, but posted back here in the hope of finding a nicer way.
I fully understand your point that task names should depend on nothing but immutable values (what I do is a temporary hack to limit the moving parts in the refactoring of the build), but still think that the configuration callback should be applied before calling whenObjectAdded: It wouldn’t break anything and is very easy to implement. But you certainly have the right to say no
It would absolutely be a breaking change. If anything we’d have to add a new callback for the new semantics. It could be useful for properties that can be set once and only once, basically forcing the user to set them on creation time. Feel free to propose an API.