I want my convention plugins to notify users that ...
# community-support
m
I want my convention plugins to notify users that they should have applied the plugin that convention plugin is for explicitly (if they only applied convention plugin without plugin to configure) Previously I relied on
afterEvaluate
but since this discussion about just how much of a legacy approach
afterEvaluate
is, I have been looking for a good solution for this. Would
BuildService
with
FinishEventListener
that throws if plugins were not applied be an idiomatic approach?
m
I still think it's fine to use
afterEvaluate {}
for improved error messages. In that other discussion there was another way to make it work without
afterEvaluate {}
by checking the classpath but if that's not possible, then
afterEvaluate {}
is fine by me
v
The question is, why does your convention plugin not simply apply the plugin in question?
m
I would much prefer explicit approach where convention plugin is just some configuration on the plugin in question to avoid repeating yourself across hundreds of modules The problem I have with convention plugins applying plugin to configure under the hood is that it's not transparent that happens And what bugs me specifically: let's say I make a common android library configuration convention plugin so this plugin applies android library plugin under the hood, now that the client engineer wants to customize some part of the android configuration they should go and refer to
android {}
extension from the plugin that isn't even in
plugins {}
block. I don't like it I would say I would only consider to apply plugins inside my convention one, if my convention plugin would expose its own extension to wrap things that should be customisable and that's is hard to account for without passing around a bunch of properties that in the end will never be used This is obviously just how I see things but I really prefer the explicitness and the idea that the convention plugin is just a useful extension not replacement
v
The problem I have with convention plugins applying plugin to configure under the hood is that it's not transparent that happens
I don't see how it is not transparent. If I apply the
my.android.library
convention plugin I would even expect that it also applies the plugin for me. Otherwise I also always have to apply two plugins when one would be sufficient. Also, you asked for the idiomatic approach. And the idiomatic approach is, if your plugin needs another plugin it should apply it.
And what bugs me specifically: let's say I make a common android library configuration convention plugin so this plugin applies android library plugin under the hood, now that the client engineer wants to customize some part of the android configuration they should go and refer to android {} extension from the plugin that isn't even in plugins {} block. I don't like it
But this is just normal and idiomatic. You also configure
base { archiveName }
without ever applying the
base
plugin explicitly usually, or
publishing { publications { ... } }
when never explicitly applying the
publishing
plugin but only
maven-publish
or
ivy-publish
. Besides that, if your client engineer does not like that, noone stops them to apply the plugin also explicitly. Applying a plugin a second time is just a no-op to support exactly this idiomatic setup.
I really prefer the explicitness and the idea that the convention plugin is just a useful extension not replacement
It is just a useful extension that also applies the plugin for you, saving another line to copy around. And well, you asked for the idiomatic approach. :-) Of course you don't have to follow the idiomatic approach, but then you should not wonder if you need to jump some hoops. :-) Despite what Martin said, I think
afterEvaluate
should be avoided under almost any cost. Each usage brings timing problems, ordering problems, and race conditions. If for example someone applies the plugin in an
afterEvaluate
block after you registered your
afterEvaluate
block, your check will complain. A build service with an operation completion listener could probably work, but I guess simpler would be to register a task that fails the build with a message and then have a
pluginManager.withPlugin("the-expected-plugin")
that sets the task to
enabled = false
and a
tasks.matching { it.name != "theFailTask" }.configureEach
where you depend on the task.
Write this in an empty build:
Copy code
afterEvaluate {
    println(pluginManager.hasPlugin("base"))
}
afterEvaluate {
    apply(plugin = "base")
}
afterEvaluate {
    println(pluginManager.hasPlugin("base"))
}
and you will see that it prints first
false
, then
true
to see what I mean. 🙂