Is it correct to say that no configuration of the publish plugin can be done in afterEvaluate?


(Justin Ryan) #1

It appears that the PublicationsExtension is forced to be configured as part of the ResolveDeferredConfigurableAction (via ExtensionContainerInternal.getAsMap()). This means that by the time afterEvaluate runs, you are no longer able to configure the PublishingExtension. Consequently, if I have a plugin that needs to be configured by the user (hence has to run in afterEvaluate), I can not influence the PublishingExtension. This seems like a flaw. This implicitly means, to me, that Extensions can not be used to affect the creation of tasks or publications, their value seems to be solely in configuring a task, since a task won’t be run till after afterEvaluate.

For example, I have an Extension for my plugin that is set by the user to tells me what to put into the publication. I seemed to be caught in an impossible situation: once I defer to afterEvaluate to get their values, I can no longer call project.extensions.configure(PublishingExtension).

Can the ResolveDeferredConfigurableAction be delayed until after the afterEvaluates are run?


(Luke Daley) #2

If I have a plugin that needs to be configured by the user (hence has to run in afterEvaluate), I can not influence the PublishingExtension.

I don’t get how this follows. That is, I don’t see why it has to be in afterEvaluate if it is user configurable.

‘afterEvaluate’ is a very large hammer and is problematic for a number of reasons. That said, it is the only solution to some problems. But it is not the solution to all problems.

Can the ResolveDeferredConfigurableAction be delayed until after the afterEvaluates are run?

Possibly, but that’s just moving the problem.

I’d like to completely exhaust options for not using ‘afterEvaluate’ first.

Can you explain a little more on why you need to use ‘afterEvaluate’?

BTW, there’s a dev list discussion going on at the moment on this area (roughly): http://gradle.1045684.n5.nabble.com/deferring-task-creation-td5711283.html

This is the most recent in a few threads on this.


(Justin Ryan) #3

Thanks for the response, some of that was born out of frustration trying to get everything running in the right order.

Concerning afterEvaluate, I’m under the firm belief that I can’t look at my own Extension until afterEvaluate. The logic being the the user applies the plugin, I add my Extension, user sets some values in the extension, then I can look at what they set. The basis for afterEvaluate is that the user’s gradle script has to be fully evaluated before I can know that they’re done setting values. For some context, here’s a dummy plugin:

class ExportExtension {
    File exportFile
}
class ExportPlugin implements Plugin<Project> {
    ExportExtension extension
      void apply(Project project) {
        extension = project.extensions.create("export", ExportExtension)
        extension.exportFile = 'internal.xml' // In lieu of a ConventionMapping
          afterEvaluate {
            // wait for extension to be configured by user
            exportInPublication(extension.exportFile) // Use file to determine publish plugin values
        }
    }
}

Here’s the usage:

apply plugin: 'myexport'
export {
    exportFile = 'customer.xml'
}

I don’t see a way of looking at exportFile without using afterEvaluate. If exportFile was solely an input of a task, then of course the task can safely see the value, but I’m trying to drive the logic of a plugin (which might create tasks) with an extension. Irrelevant of the publish plugin, what if I just want to create tasks based on a value provided from the user?


(Luke Daley) #4

Generally, that’s what the current discussion that I linked to is about.

It’s hard to provide general answers to such general questions. I don’t mean that as a cop-out, I mean that it depends on exactly what you are trying to do on how to proceed. In general, the alternative is the use of lazily evaluated data structures, convention mappings, and careful extension API design.

The problem with afterEvaluate() is that it’s too coarse. You end up pushing all sequencing problems to the end.

However, we are getting off topic…

afterEvaluate() is supposed to fire after all configuration is done. Not configuring deferrables by this time doesn’t really fit in with that. I’m not for it. I’ll propose it internally though and post back.


(Justin Ryan) #5

I don’t think it’s that generic of a question. How does one read an Extension value set by a user in a Plugin? Specifically in a phase which isn’t execution.

If you read the extension during the apply(), then the user hasn’t set the values yet. If you re-act in the extension in set methods, then you might be working with the wrong value since the user could set the value many times.

It’s feeling like only Extensions which work like a NamedDomainObjectContainer can be used to create tasks. Since the plugin can register callbacks with DomainObjectCollection.all which are immediately called when the user calls them, which would be before the afterEvaluate.


(Luke Daley) #6

I don’t think it’s that generic of a question. How does one read an Extension value set by a user in a Plugin?

The strategy to date has been to use convention mapping, which only works for 1-1 mappings. Most of the time we design around this.

The deferred configuration stuff we are doing with publishing is an attempt to provide a better way, though as you have found it has its own problems.

I don’t have a good answer for you at the moment unfortunately. We are still searching for a comprehensive solution. If you’re up for it, I recommend reading the spec on this stuff: https://github.com/gradle/gradle/blob/master/design-docs/lazy-configuration.md and giving feedback via the dev list.

There aren’t any big changes planned for 1.7, but I expect there will be something in 1.8.