project.afterEvaluate() should execute the action if the project is already evaluated


(Dimitar Dimitrov) #1

I have a few tasks which inherit from AbstractExecTask, and determine most of the parameters from a domain object maintained externally (let’s call it “instance”). All these tasks inherit from a base class like this:

abstract class PgExec<T extends PgExec> extends AbstractExecTask<T> {
    PgSqlInstance instance
    String executableName = 'pg_ctl'

    PgExec(Class<T> taskType, String description) {
        super(taskType)
        this.group = 'pgSql'
        this.description = description

        project.afterEvaluate {
            configureExec()
            configureInstance(instance)
        }
    }

    abstract void configureInstance(PgSqlInstance instance);
    ...
}

I added a few rules to auto-create these tasks based on the domain object collection (i.e. for each instance I want to add a set of tasks). What I found is that all of a sudden the project.afterEvaluate { ... } stopped firing.

After a more careful look I realized that the tasks are created after the project has been evaluated, because for some reason that’s when rules were called. This forced me to rewrite the code above as:

abstract class PgExec<T extends PgExec> extends AbstractExecTask<T> {
    PgSqlInstance instance
    String executableName = 'pg_ctl'

    PgExec(Class<T> taskType, String description) {
        super(taskType)
        this.group = 'pgSql'
        this.description = description

        project.afterEvaluate {
            configureExec()
        }
    }

    void setInstance(PgSqlInstance instance) {
        this.instance = instance
        if (project.state.executed) {
            configureExec()
        }
    }
   ...
}

I find that quite ugly, and inconvenient. Typically I can rely on the declarative nature of Gradle (i.e. if I do instances.all { ... } I am guaranteed that all instances, current and future will be configured in this way).

Is there an idiomatic way to achieve what I am after (i.e. to use the latest and greatest values from instance)?

Does it make sense to change project.afterEvaluate { ... } to run the closure immediately if the project has finished evaluation? If not, how about outputting a warning, or an error that the action was ignored?