Using subprojects { ... }, allprojects { ... }, project("...") { ... }, or any other means to do cross-project configuration is bad practice and should be avoided.
It immediately adds project coupling and disturbs or disables some more sophisticated Gradle features or optimizations.
So your option 1 it is. Convention plugins in buildSrc or an included build, for example implemented as precompiled script plugins, that you apply to the projects where you want their effect to be present directly.
Björn,
type of your opinion is ’ Roma locuta, causa finita’ - I have delegated the propagation of the property to the convention plugin. However the value of the property I left evaluated in the root script and redeclared there in the ext var block.
What convinces me is the “sophisticated Gradle features or optimizations” capabilities.
thanks a lot.
T.
PS.
For custom test tasks like: task customTestTask(type: Test)
do I need to set up the property from the test prop map to custom prop map in that way:
systemProperty ‘propKey’, test.getSystemProperties().get(‘propKey’)
That’s not an opinion, but a fact.
Cross-project configuration is evil and should be avoided whereever possible.
That’s how it is and what also the Gradle folks say, for example in the userguide at Sharing Build Logic between Subprojects.
redeclared there in the ext var block.
Practically any usage of ext or extra properties is also bad practice and just a work-around for doing something not the “right” way.
or there is some implicit way to get it set.
If you want to set a system property on all tasks of type Test, you can do so using for example tasks.withType(Test).configureEach { ... }. (don’t leave out the configureEach even if it works or it breaks task-configuration avoidance for all tasks of type Test)
There is no automatic inheriting from anything from default test tasks to custom test tasks, as that might as well be not wanted, Gradle simply cannot know.
Maybe in the nice world where you move your perfect Maven project (build of pretty independent subprojects) to Gradle this advice is easy to follow, but e.g. when you must migrate old Ant project with its legacy drawbacks you must use some “work-arounds”.
The question is what work-around mechanism to use not to get into bigger troubles.
Let’s say (like above) you must provide some common properties for subprojects’ tests.
As you said: the fact is that the best way to provide them is to use buildSrc convention plugin. So far so good.
But where the properties should be “born”?
For many reason the convention plugin seem to be not the right place for it:
a) the plugin would repeat I/O operation to read/load the properties data while in the root script you just read them once
b) the plugin is not capable to evaluate the properties as it just has not enough context. Providing the plugin with broader context would be just ugly solution.
So the root script seems to be the best place to evaluate the properties.
But the final question is how to pass them from the root to the plugin, in a safe way, ‘not disabling sophisticated Gradle features or optimizations’.
when you must migrate old Ant project with its legacy drawbacks you must use some “work-arounds”.
There’s usually always a way to do it differently.
But as long as you are aware those are unclean work-arounds and it works for you, great.
Let’s say (like above) you must provide some common properties for subprojects’ tests.
Depends on the gory details.
If they are for example static values, put them to gradle.properties in the root project.
If you need to do some calculations, maybe a shared build service would be an appropriate way, as it is build-run-scoped, so the calculation would only be done once and can be used by multiple projects safely.
Depending on extra properties in the root project should latest be a problem when isolated projects become production-ready.