Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make Gradle extensions lazily evaluate properties that are set dynamically by tasks?

I'm pretty new to working with Gradle and I'm trying to develop a plugin that helps manage version numbering. This plugin defines a task that sets the project.version property of the project it's applied to.

What I'm trying to do is make it so that this property is set at the start of every Gradle build. Using Peter's answer to another Gradle question, I've managed to get my task to execute before any other by adding gradle.startParameter.taskNames = [":setProjectVersionNumber"] + gradle.startParameter.taskNames within my plugin's apply method.

However, other plugins (notably 'Maven-publish') rely on the version being specified during the configuration phase:

publishing {
    publications {
        somePublication(MavenPublication) {
            version = project.version
        }
    }
}

What I'd like to know is if there's a way that I can make the evaluation of properties like version within these extensions as lazy as possible - such that they're not evaluated until a task that depends upon them is called, which in this case might be :publishToMavenLocal.

Below is an SSCCE that demonstrates what I'm hoping to achieve:

// This would be included within the plugin
class SetProjectVersionNumber extends DefaultTask {

    @TaskAction
    void start() {
        // This will set project.version during execution phase
        project.version = "1.2.3"
        logger.info "Set project version number: $project.version"
    }
}

task setProjectVersionNumber(type: SetProjectVersionNumber)

// Imagine this block being replaced by a maven 'publishing' block (or something similar)
ext {
    version = project.version

    // This will print 'unspecified', as it's evaluated during configuration phase
    println "In extension, setting version=$project.version"
}

If you can provide a way to make ext.version equal 1.2.3 in the example above, I believe you've resolved my issue.

If this is asking too much, it may be possible for me to make my plugin generate the version string at configuration-time rather than execution-time. It would be nice to know if I could do it this way, though.

EDIT

In an experimental branch, I tried moving all the version string assignment logic to the configuration-phase (by making it all happen during plugin application rather than during task execution), but I don't believe this will work as the plugin extension has not yet been processed and trying to refer to properties defined in it fail.

EDIT 2

Wrapping the version string assignment logic in a project.afterEvaluate closure seems to have worked:

@Override
public void apply(Project project) {
    logger = project.logger
    project.extensions.create(EXTENSION_NAME, SemVerPluginExtension)

    project.afterEvaluate {
        setVersionProjectNumber(project)
        addTasks(project)
    }
}

In a mock project, I implement build.gradle as follows:

apply plugin: 'semver'
apply plugin: 'maven-publish'

group = 'temp'

buildscript {
    repositories {
        mavenLocal()
        jcenter()
    }
    dependencies {
        classpath 'com.github.tagc:semver-plugin:0.2.2'
    }
}

semver {
    versionFilePath = 'version.properties'
}

publishing {
    publications {
        testPublication(MavenPublication) {
            version = project.version
            assert version
            println "Set publication version to $version"
        }
    }
}

For some reason, this seems to work. Although the version string assignment logic is wrapped in an 'afterEvaluate' closure and the test publication version assignment isn't, the former still occurs before the latter:

Compiling build file '/Users/davidfallah/Documents/semver/TestSemver2/build.gradle' using StatementExtractingScriptTransformer.
Compiling build file '/Users/davidfallah/Documents/semver/TestSemver2/build.gradle' using BuildScriptTransformer.
VERSION FILE PATH=version.properties
Current Git branch: develop
Set project version to 0.2.1-SNAPSHOT
Set publication version to 0.2.1-SNAPSHOT
All projects evaluated.

I'm leaving this question open and unresolved since I'd still like to know if it's possible to do it the way I originally intended. Additionally, I'd appreciate any explanation about why the publication version is assigned after the project version is set, and whether I can depend on that always being the case or whether that's just happening now by accident.

like image 773
Tagc Avatar asked Dec 29 '14 02:12

Tagc


1 Answers

You can use lazy instantiation of GStrings to evaluate properties at run time:

project.tasks.create("example_task", Exec.class, {
    commandLine 'echo', "${-> project.someproperty}"
})

Note that you have to use quotation marks and not apostrophes - "${...}" works, but '${...}' does not.

like image 62
Rob Watts Avatar answered Oct 23 '22 06:10

Rob Watts