Using PropertyState during Configuration Phase

I am trying to learn PropertyState and use extension values during “Configuration” phase. Wrote a sample program. Works fine for “Execution” phase. But getting “null” for “Config” phase.

Execute the program using ** “gradle print” **

class pluginConfig {
final PropertyState product
pluginConfig (Project project) {
product = project.property(String)
}
}

class pluginTask extends DefaultTask {
pluginTask() {
println “Config phase : ${project.cfg.product}”
}
@TaskAction
void printGreeting() {
println “${project.cfg.product}”
}
}

class superPlugin implements Plugin {
void apply(Project p) {
p.extensions.create(“cfg”,pluginConfig, p)
p.tasks.create(“print”,pluginTask)

}

}

apply plugin: superPlugin
cfg {
product=‘Welcome’
}
dependencies {
gradleApi()
}

Output

Configure project :
Config phase : value: null

> Task :print
value: Welcome

Expecting:

Configure project :
Config phase : value: Welcome

> Task :print
value: Welcome

pluginTask’s constructor is called during application of the plugin (when tasks.create is called), but cfg.product is not set to ‘Welcome’ until after the plugin is finished being applied.

If you haven’t already seen it, there’s a section in the user guide on providers and property state that may be useful.
https://docs.gradle.org/current/userguide/custom_plugins.html#sec:mapping_extension_properties_to_task_properties

NO
I already read that URL. The URL provides example of using PropertyState in TaskAction and not in Config phase.

Tell me the solution instead of URL. Please provide modified code if possible.

For example: In above scenario I want to create some folders in config phase with the product name. How to do that ? I don’t want folder creation in @TaskAction.

#1. Do you say that config phase is different from Constructor for a Task ?. The output line clearly says the constructor is executed during Config phase.

Configure project :
Config phase : value: null

#2. How does PropertyState variable differs from String variable if it is not used during config phase ?

#3. Do you say there are 3 phases now instead of 2 (Config & Execution phase)?
Configuration of Plugin / Creating tasks & Invoking constructors of tasks
Application of Plugin
Execution phase

No, that’s not what I am saying. What I’m saying is that the build script is executed from top to bottom during the configuration phase. So, we have the following flow:

  1. The configuration phase executes the build script, effectively like any other Groovy script would be executed.
  2. Ignoring the definitions of the classes, since those are not “executable”, the first operation is to apply the plugin.
  3. The plugin class’s apply method is called.
  4. The extension is created, with a default value of null for product.
  5. The task is created, which results in the constructor being called, which prints the current value of product, resulting in “null”.
  6. Applying the plugin is complete, so the build script resumes and sets the value of product to ‘Welcome’.

Sometime later, the task execution phase starts and the print task’s TaskAction method executes. product is now set to ‘Welcome’ and we get the value.

The above flow has nothing to do with PropertyState, you would get the same results with a String.

I know the documentation I linked you to is not ideal and could use more details, but what the example code is trying to express is how the hello task’s message property is linked to the greeting extension’s message property (this linkage occurs in the tasks.create closure; message = extension.message). If the task’s message value is set, then that value is used. If the task’s message value is not set, then the value from the extension is used. I believe there’s some runtime class decoration magic for PropertyState happening, which I think is explicitly shown (the setMessage methods) in the 4.0 release notes example.

There are 3 phases, but not in the way you are thinking here. There is an initialization phase that sets up multi-project builds and other init type work.

Well, I’m not an expert in this area, I was just trying to offer you some help with what I knew about.

Thanks for your reply. I have the following scenario:

Now I want to set some variables, create folders after Applying the Plugin, but before execution of Tasks.

Any Suggestions ?. do I need to use afterEvaluate block ? or any other code for this ?

Ya, project.afterEvaluate is probably the way to go. There’s also project.gradle.taskGraph.whenReady.
https://docs.gradle.org/current/javadoc/org/gradle/api/execution/TaskExecutionGraph.html#whenReady(groovy.lang.Closure)

Out of curiosity, why do you want/need to create folders between the config and exec phases?

Example Scenario’s:

#1. I wanted a common folder under build to Store all my task input and output files.
Example:

build.gradle file

productName=myoutputfolder

output

Task1
build/myoutputfolder/task1Output.txt

Task2
build/myoutputfolder/task2Output.txt

CleanTask
delete myoutputfolder

#2. I want a common url which is used by all Tasks. Based on URL availability I need to enable/disable some Tasks to plugin Customers.

Exec phase is more specific to a particular Task. I was looking for a common phase from where I can configure, validate common pre-requisites for all myTasks…

Any Further suggestions ?

#3. Does afterEvaluate works good in multi-project environments. Which project goes to this function first ? The current project or root project.

I put together a working example where a task (displayGreeting) has configuration that uses work done with the execution of the task that it depends on (createGreeting), using Provider/PropertyState (use Provider/Property for 4.3 and above) pattern.

apply plugin: 'base'

task createGreeting (type: CreateGreeting){
  greetingText 'Hello from inside createGreeting'
}

task displayGreeting (type: DisplayGreeting) {
  dependsOn createGreeting
  greeting createGreeting.greeting
}



class CreateGreeting extends DefaultTask {
  @Input String greetingText
  @OutputFile File getGreetingFile() {
    project.file("$project.buildDir/greeting.txt")
  }

  Provider<String> getGreeting() {
    project.provider {
      greetingFile.text
    }
  }

  @TaskAction doit() {
    greetingFile.text = greetingText + """
"""
  }
}

class DisplayGreeting extends DefaultTask {
  final PropertyState<String> greetingProp = project.property(String)

  String getGreeting() {
    this.greetingProp.get()
  }

  void setGreeting(Provider<String> greetingProv) {
    this.greetingProp.set(greetingProv)
  }

  @TaskAction doit() {
    println greeting
  }
}