Pass variable back from Groovy class to Gradle task - gradle

I might not have set the right title for this question and that's because i'm not that advanced in Gradle, so apologies for that.
I have the below Gradle tasks in my build.gradle:
// buildSrc/src/main/groovy/envs/actions - This where all the groovy classes which I use are located
import envs.actions.*
tasks.create("createEnvironment", CreateApplicationEnvironmentTask) {
println "executing creation"
}
return tasks.create("createRecord", CreateRecordTask) {
dependsOn "createEnvironment"
varEbCname = tasks["createEnvironment"].ebCname
}
The first task is of type "CreateApplicationEnvironmentTask", which is a class in a .groovy file where i do some actions and set some variables.
One of these variables that i set in the "CreateApplicationEnvironmentTask" class is also one called "ebCname", as seen below:
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
class CreateApplicationEnvironmentTask extends DefaultTask {
String ebCname
#TaskAction
def create() {
ebCname = "some_value"
}
}
What I'm looking to do is to be able to get the value of the variable "ebCname"(which is set inside the class) from the second task "createRecord" by calling the "createEnvironment" task. I'm doing the below from within the second task, but it's not working:
varEbCname = tasks["createEnvironment"].ebCname
"varEbCname" ends up being null.
I also tried "return"-ing the "ebCname" variable from the class, but that didn't work either.
The "ebCname" variable is set in the "CreateApplicationEnvironmentTask" class which is used by the "createEnvironment" task, so I need a way get the value of that variable from the second task "createRecord".
Any idea why this isn't working and how would I go about doing this?
Thanks,

I think the main confusion here is between the gradle task life cycle phases. This is quite common when starting out with gradle. I would recommend reading through the above link to get a feel for the phases.
To illustrate here is a complete build.gradle which more or less mirrors your code:
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
task createEnvironment(type: CreateApplicationEnvironmentTask) {
println "configuring CreateApplicationEnvironmentTask"
doLast {
println "execution phase (doLast) CreateApplicationEnvironmentTask"
}
}
task createRecord(type: CreateRecordTask, dependsOn: ['createEnvironment']) {
println "configuration phase value: ${tasks["createEnvironment"].ebCname}"
doLast {
println "execution phase (doLast) value: ${tasks["createEnvironment"].ebCname}"
}
}
class CreateApplicationEnvironmentTask extends DefaultTask {
String ebCname
#TaskAction
def create() {
println "execution phase (class) CreateApplicationEnvironmentTask"
ebCname = "some_value"
}
}
class CreateRecordTask extends DefaultTask {
#TaskAction
def doit() {
println "executin phase (class) CreateRecordTask"
}
}
executing:
~> gradle createRecord
prints:
configuring CreateApplicationEnvironmentTask
configuration phase value: null
:createEnvironment
execution phase (class) CreateApplicationEnvironmentTask
execution phase (doLast) CreateApplicationEnvironmentTask
:createRecord
executin phase (class) CreateRecordTask
execution phase (doLast) value: some_value
BUILD SUCCESSFUL
when you execute any gradle task on a build.gradle file (or even just execute gradle tasks to print the available tasks), the build file is evaluated and the configuration phase code is run. This includes the code inside the curlies of any task foo { ... } definition, even if that task is not executed.
In contrast, the code inside the doLast block task foo { doLast { ... }} is executed in the execution phase.
The reason you were seeing a null value is that you set the property ebCname in your task action method (execution phase), but you are printing it in the configuration phase. Thus it has not been set yet. Either setting the value in the configuration phase (in the constructor of the class, directly when you declare the field, of inside the task declaration
task createRecord(type: CreateRecordTask...) {
ebCname = "foo"
}
) or referring to the value in the execution phase will give you a non-null value.

Related

Groovy DSL convention object "Could not get unknown property"

I have the following code in my build.gradle:
class GreetingPlugin implements Plugin<Project> {
def void apply(Project project) {
project.convention.plugins.greeting = new GreetingPluginConvention()
project.task('hello') {
doLast {
println project.convention.plugins.greeting.message
}
}
}
}
class GreetingPluginConvention {
String message
def greet(Closure closure) {
closure.delegate = this
closure()
}
}
apply plugin: GreetingPlugin
greet {
message = 'Hi from Gradle'
}
It executes nicely - ./gradlew hello prints "Hi from Gradle" which is expected.
However, using variable greet in the script (e.g. println greet) produces "Could not get unknown property 'greet' for project ':app' of type org.gradle.api.Project."
My question is - how the 'greet' variable is found when called against closure, but not found when used as a regular variable. What Groovy/Gradle magic is happening behind the scenes?
When it's called in a closure,
greet {
message = 'Hi from Gradle'
}
you are effectively adding more code to the original greet block/closure defined in GreetingPluginConvention, it is not a variable so attempts to treat it a such fail. Think of these block closures as a handy way to set or configure your plugins.
Gradle scripts are a bit different than your average linear script.

Set the properties in the configuration phase of a task in another task

I have spent the last few hours trying to find a solution for my requirement, without luck:
I have a task that has to run some logic in a certain path:
task run(type: MyPlugin) {
pathForPlugin = myPath //Defined as a property in another gradle file
}
I want to set the "pathForPlugin" property dynamically in another task because it has to be read from some configuration file.
task initPaths(type: PathFinder) {
configurationFile = 'C:\\myConfig.conf'
}
The myConfig.conf would look like this:
pathForPlugin = 'C:\\Correct\\Path'
The problem is that "initPaths" has to run before the configuration phase of "run".
I have tried several approaches for this (GradleBuild task, dependsOn, Using Properties in the Plugin for "Lazy Configuration") but every approach only takes effect in the Execution phase leading to the "pathForPlugin" always staying at its default value.
Is there some way i can realize this or should i look for another solution outside of the gradle build?
I found a solution for the problem:
Instead of defining a task "initPaths" i directly used the java class "Pathfinder" in the build script:
import mypackage.PathFinder;
new PathFinder(project).run()
You only have to make sure that this part is above the definition of the task where the properties are used.
I admit this is a bit of a "hacky" solution but it works fine for my requirement.
you can do like this:
ext {
myPath //use it as a global variable that you can set and get from different gradle tasks and files
}
task firstTask {
doLast {
ext.myPath = "your path"
}
}
task run(type: MyPlugin) {
doFirst { //executed on runtime not on task definition
pathForPlugin = ext.myPath //Defined as a property in another gradle file
}
}
//example 2 - create run task dynamic
task initPath {
doLast {
tasks.create(name: "run", type: MyPlugin) {
pathForPlugin = ext.myPath
}
}
}

Gradle Task . "(type: Copy)" and <doLast> can't both work

task simpleTask{
print("simpleTask is reach");
}
task copySomeFile(type: Copy){
print("copySomeFile is reach");
from baseProjectPath;
into toProjectPath;
appendXML();
}
def appendXML(){
//modify a.txt
}
//i just want to run "simpleTask" only, but when "gradle simpleTask", the task"copySomeFile" will be run also ! I know beacuse gradle initialization.
but if write like this
task copySomeFile(type: Copy)<<{
}
the "copySomeFile" will not work.
it seems like "(type: Copy)" can't work with the "<<" or "doLast{}"?
i just want "--gradle simpleTask" "--gradle copySomeFile" can run alone.
You have to read about Gradle build lifecycle.
There are 2 phases you should note - Configuration and Execution. All tasks are always been configured on every build, but only some of them are really executed as the Execution phase.
What you see is that copySomeFile task was configured during the configuration phase. It doesn't copy anything, but it has to be configured. And everything within a tasks closure is task's configuration, that is why you see results of the print("copySomeFile is reach"); in the output.
<< or doLast are used to run something at the Execution phase, but your task of type Copy will not be configured if you place all it's configuration into doLast section or add << to the task definition - that is the reason why copy doesn't work.
Yeh, i got it. How much I appreciate both of you. SHARE THE CODE:
task simpleTask {
print("\nsimpleTask is configured"); // executed during the configuration plase, always
doLast {
print("\nsimpleTask is executed"); // executed during the execution plase, only if the simpleTask is executed
}
}
task copySomeFile(type: Copy) {
print("\ncopySomeFile is configured"); // executed always,执行其他任务时,此代码也会执行
from "D:/a.txt";// not executed. 执行其他任务时,此代码不会执行
into "D:/b.txt";// not executed. 执行其他任务时,此代码不会执行
doLast {
appendXML(); //only this task executed, the appendXML executed. 只有此task执行时,才会执行.比如(gradle copySomeFile);
}
}
def appendXML(){
print("\nappendXML");
}

Task of Type Copy not executed

I am facing an issue executing my task of type Copy,:
Skipping the task as it has no source files
I get if I run in the debug mode.
My Plugin.groovy class (where the call to the plugin task in made )
Task task = project.tasks.create("makeJarPlugin", MakeJarPluginTask.class)
task.dependsOn("clearDistPlugin", "build")
My MakeJarPluginTask.grrovy
class MakeJarPluginTask extends Copy {
#TaskAction
def makeJar(){
logger.lifecycle("creating a jar *********************")
delete('dist/')
from('build/intermediates/bundles/release')
into('dist/')
include('classes.jar')
def jarName = new VersionName().getNameWithVersion() + '.jar'
rename('classes.jar', jarName)
}
}
Now, I execute this task in my android studio project using
gradlew makeJarPlugin --info
It gives me the output:
Skipping task ':network:makeJar1' as it has no source files.
makeJar1 UP-TO-DATE
There is something wrong with the type Copy as in the same way I execute my delete task and it executes. Any pointers!
It seems that this answer may be helpful.
Hint: you need to configure the task. Otherwise it won't be executed since the whole configuration is done in makeJar which is too late since this is an action.
Instead of using copy you can also try:
class MakeJarPluginTask extends DefaultTask {
#TaskAction
def makeJar() {
logger.lifecycle("creating a jar *********************")
delete('dist/')
project.copy {
from('build/intermediates/bundles/release')
into('dist/')
include('classes.jar')
def jarName = new VersionName().getNameWithVersion() + '.jar'
rename('classes.jar', jarName)
}
}
}

How to use gradle extension correctly in plugins using GradleBuild task?

EDIT : I rephrased my question in taken the propositon of David M. Karr into account.
I am writing a gradle plugin. This plugin is launching a task extending GradleBuild. The external gradle build file needs some info as parameters. These parameters are given in project extension.
Plugin code
class MyPlugin implements Plugin<Project> {
def mExt
void apply(Project project) {
mExt = project.extensions.create('myext',MyExt)
project.task('myTask', type:GradleBuild){
def param = new StartParameter()
param.setProjectProperties([target:getTarget()])
// Problem here
startParameter = param
buildFile = getMyBuildPath()
tasks = [
'build',
'generateDebugJavadocJar'
]
}
}
def getMyBuildPath(){
...
}
// Problem here
def getTarget(){
return {mExt.target}
}
}
class MyExt {
def String target = "uninitialised"
}
Gradle build file :
apply plugin : 'com.example.myplugin'
ext{
target = "myTarget"
}
External Gradle build file :
task build(){
println project.target
}
If I put a closure in getTarget(), println project.target shows the closure and not the string.
If I don't put the closure :
// Problem here
def getTarget(){
return mExt.target
}
Then I got "uninitialised" instead of "myTarget".
How can I get the value of myext.target here ?
I am using gradle 2.3
Try this:
Define an instance variable called "myext", of type "MyExt".
In the "apply" method, do this:
myext = project.extensions.create('myext',MyExt)
In the "getTarget" method, return "myext.target".
I have succeeded in getting what I wanted to in using project.afterEvaluate method. Thanks to this question
1) In gradle build task, startParameter.projectProperties is waiting for a map, not a closure. So the idea to put a closure for a lazy definition cannot work.
2) If I put directly in my plugin a reference to mExt.target or project.myext.target, then the initial value is set. The value put in my build.gradle file is not used because the plugin is already evaluated.
3) project.afterEvaluate() solve my problem. The closure ends configuring myTask in using the project's extension.
void apply(Project project) {
project.extensions.create('myext',MyExt)
project.task('myTask', type:GradleBuild){
buildFile = getMyBuildPath()
tasks = [
'build',
'generateDebugJavadocJar'
]
}
project.afterEvaluate { proj ->
proj.myTask.startParameter.projectProperties = [target:proj.myext.target]
}
}

Resources