I am trying to write a plugin and make use of the plugin convention. The examples I have looked at (for example izpack, gradle’s application plugin) seem to involve a lot of copying of values from the convention to a task and then on to an object that implements them. For example the application plugin defines fields in the ApplicationPluginConvention, then in ApplicationPlugin copies them from the ApplicationPluginConvention to the CreateStartScripts task which then copies them to the StartScriptGenerator which actually does the work.
Is this considered the best way to do this? It feels like a lot of boilerplate copying to me, but I am new to groovy and to writing anything but the simplest of gradle tasks, so I suspect I am missing something.
Is there a reason to not just pass the entire convention to the task/object? Is there a reason to not access the convention outside of the plugin? It seems like that would reduce much of the code that looks like: startScripts.conventionMapping.mainClassName = { pluginConvention.mainClassName } or generator.mainClassName = getMainClassName()
A central idea behind convention mapping is to separate the configuration of tasks by plugins from the tasks themselves. Tasks shouldn’t make assumptions about the world. They shouldn’t know about things like source sets, configurations and convention objects. All of that would make them less reusable and tie them to the plugin.
Additionally, convention mapping is one of the ways to tackle the evaluation order problem, where a value used to configure a task might not yet be available at the time the plugin executes.
Ideally, a convention/extension is on a higher abstraction level than the task and makes it easy to solve the common use cases. Whether the task delegates somewhere else is an implementation detail of the task.
By the way, in general you should favor extensions (introduced in 1.0-m3) over convention objects.
As an addition to what Peter said, a feature of convention mappings is that the allow values to be specified as the result of computations, which facilitates flexible conventions.
With the first example, the task is going to write to the standard build dir because it doesn’t pick up the change to the ‘buildDir’ after the convention mapping is wired in.
With the second example, we wire in a computation that is executed when the value is read (i.e. lazily) so the change to this value after the convention mapping is wired in is effective.
Ah, I see now. I wasn’t paying attention to the extra { } in the task configuration. I am feeling my lack of groovy-ness, not noticing that these task fields were being pulled from the convention lazily. The way the ApplicationPlugin was written makes much more sense now.
I have also switched to the extension mechanism and it seems to be working well, so thanks for the pointer.