Configuration execution order

(Ari Maniatis) #1

This is going to be slightly involved, so I’ll try and explain it slowly and carefully. I’m working on a new plugin for releasing projects. It is a little different to other release plugins since it generates the project version string from SCM. Here is the code:

But that’s not important right now. More important is the way it is invoked from build.gradle

apply plugin: 'release'
  release {
 scm = 'svn'
 failOnSnapshotDependencies = true

Because we don’t know which scm to use at the point that “apply” executes, the plugin defers setting project.version until the ReleasePluginExtension.setScm(string) is invoked with ‘svn’. At that point we are able to set the correct SCM and fetch the version from it. For example, for svn that might be “trunk”.

Now the problem is that the release closure (seen above) is executed very late in the configuration lifecycle of gradle. So the project.version isn’t set until too late for other code. How can I move this sooner earlier?

  1. Write separate plugins for svn/git/etc. Invoked with something like:

apply plugin: ‘release-svn’

  1. Somehow passing a parameter to the apply “constructor” of the plugin

apply plugin: ‘release’, scm:‘svn’

Not understanding how “apply” is invoked I don’t understand if such a hack is viable.

  1. Some other trick I cannot think of.

Thanks for any help. I am new to both gradle and groovy.

(Peter Niederwieser) #2

All access to mutable properties of the Gradle object model (e.g. ‘project.version’) must be deferred to at least the end of the configuration phase (because until then, one is free to make changes). In that sense, if ‘project.version’ is set too late for some other code (but still in the configuration phase), than that other code has a bug.

The SCM should only be accessed in the execution phase of the build, i.e. by a running task. Doing so when ‘setScm()’ gets called would be too early.

(Ari Maniatis) #3

Thanks for this answer. I’ll review the other plugin to try and understand what it is doing (I have a problem with the java manifest creation).

But the SCM must be accessed in the configuration phase for my plugin to work, since it derives the project.version from the SCM state. The whole point of my approach is to link the version to the project position within SCM. Are we on trunk or some other branch? If we are on a tag then we assume we are building a release and adjust the version appropriately.

I think this approach has a lot going for it:

  • it avoids the maven release plugin approach of three commits for every release * it avoids changing the source code (or gradle properties) just to tag a release * it keeps versioning information where it belongs: in the version control system

(Peter Niederwieser) #4

I see. My concern was that it would slow down the build, as it will run for every Gradle invocation whatsoever. Just keep that in mind.

(Ari Maniatis) #5

Since we are only getting client-side properties (not using the SCM to fetch values across the network) it seems quite fast in my testing.

I am still struggling with ordering, particularly in a multi-module build. See this test here:

When I use project.configurations to apply the configuration closure, the setScm() method isn’t called at all before the tests run, even for just the parent project. But when I use project.allprojects to apply it, then I get different behaviour and all projects get setScm() called. Are these two calls different (apart from the obvious fact that one applies to the parent and the other to the parent and children)?

(Peter Niederwieser) #6

‘project.configurations’ is something entirely different; it’s a concept used in dependency management (think of it as a named class path). You are probably configuring a configuration named ‘release’ here, not your ‘release’ extension.

(Ari Maniatis) #7

Thank you for that clarification. Perhaps you could add that distinction to

allprojects(Closure configureClosure) This method executes the given closure against this project and its sub-projects. The target Project is passed to the closure as the closure’s delegate.

configurations(Closure configureClosure) Configures the dependency configurations for this project. This method executes the given closure against the ConfigurationContainer for this project. The ConfigurationContainer is passed to the closure as the closure’s delegate.

It is not clear from that how they differ.

I’m guessing apply(Closure closure) might be the equivalent to allprojects but just for the parent. I’ll give it a try.

Thanks for all your help

(René Groeschke) #8

apply(Closure closure) is not an equivalent for allprojects. The apply method is used for applying plugins to a project.

cheers, René

(Ari Maniatis) #9

So there is no equivalent to the allprojects method which applies just to the parent?

(Peter Niederwieser) #10

‘apply’ applies a plugin to the current project. ‘allprojects’ configures all projects - it doesn’t apply a plugin. They are completely different things. I recommend to study the general chapters of the Gradle User Guide as well as the chapter on multi-project builds.

(Ari Maniatis) #11

Thanks Peter. I am a bit beyond that level of knowledge now and am looking for something that lets me apply a configuration to just the parent project and none of its children, within a unit test.

If you see these tests here: I’d like to write another test which pushes the config to the parent and not the children within the unit test. I’m just trying everything in Project which seems to take a closure as an argument to see if it will do the trick, but to no avail.

Thanks for all your help… I appreciate it.