Custom plugin in a multiproject build


(Michael Saladin) #1

I am not sure how I should design a custom plugin that has the following properties:

  • Step 1: It should do something in every subproject (e.g. gather which dependencies are used)
  • Step 2: At the end, in the root project, it should take all the dependencies of all subprojects (the result of Step 1) and upload them to a WS

We had a similar concept in ANT, and there it was quite easy because we iterated manually (e.g. manually means that we implemented the looping logic within ANT) over all subprojects, and so it was simple to do this. But in Gradle, I am a little bit lost. Let’s say I want to add this custom plugin at the end of the assemble task, what I do is this:

assemble.finalizedBy ('task-of-my-custom-plugin'')

But when I execute “gradle assemble” in the toplevel (root) project, the order of execution is like this:

:buildSrc:assemble UP-TO-DATE
:assemble UP-TO-DATE

So this means that when excuting “assemble” on the top-level project, the custom task cannot access the output of the custom tasks in the subprojects, because the top-level task was already executed… Actually, IMHO this is quite a common pattern for multi-project builds:
Step 1: Do something in all subprojects
Step 2: Do something with the result of step 1

Step 1: Execute all the tests in the subprojects
Step 2: Gather all test reports from all subprojects and do something with it, e.g. count the number of errors and insert them into a database

Another example:
Step 1: Execute some special JS Karma tests in some Javascript subprojects
Step 2: Gather the Karma reports and do something with them

So for me this is a common pattern and I don’t know how to implement/design this with Gradle. It seems that the order-of-execution prevents this pattern… Any ideas how to design this? I see the following alternatives that I don’t really like:

Alternative 1:

  • Just use a separate target name, don’t include it into any build workflow
  • So the user and the continous-build-server would need to invoke the custom-plugin separately
  • Not so good, because it gets forgotten (at least by the end-user who invokes the build manually on cmd-line)

Alternative 2:

  • Change the custom-plugin to manually loop through all subprojects and do it there
  • So the plugin task knows that it is invoked from the root project, and then it does it actions, but if the plugin task is invoked inside a subproject, it does nothing
  • This solution seems a little bit complex to me, the plugin then knows something about the overall build architecture

Any other ideas?
Thanks a lot

(Stefan Oehme) #2

Task execution order is determined by the dependencies of your tasks. If your root project needs to access the outputs of all the subprojects, it needs to add these outputs as an input to your aggregation task.

Also, you probably want to split the aggregation logic into a different plugin (e.g. test-report-aggregator). This way, users can decide themselves at which level they want to aggregate instead of your plugin doing some if(rootProject) check.