I am new to gradle and groovy,I am reading the usr guide of Gradle, and have some syntax questions on task method:
task intro(dependsOn: hello) {
doLast { println "I'm Gradle" }
}
Question 1:in above code, which method is called in Project API ? I know there are four overload in API:
Task task(String name, Closure configureClosure);
Task task(Map<String, ?> args, String name, Closure configureClosure);
Task task(Map<String, ?> args, String name) throws InvalidUserDataException;
Task task(String name) throws InvalidUserDataException;
but the parameter such as intro(dependsOn: hello) or copy(type: Copy) make me confused, what it should be if add parentheses?
Question 2: why << is shorthand for doLast method? I mean there is a leftshift method in Task API ? what is diff between them?
Question 3: why can use tasks.create() method in build.gradle 17.1. Defining tasks,I did not see tasks property in Project API or in AbstractProject source code.
In this particular case:
task intro(dependsOn: hello) {
doLast { println "I'm Gradle" }
}
the following method will be invoked:
Task task(Map<String, ?> args, String name, Closure configureClosure);
Since gradle uses a specific DSL it may be hard to tell but:
Q1
intro is a String name argument
dependsOn: hello which is equivalent to [dependsOn: hello] (a Map) is Map<String, ?> args
{ doLast { println "I'm Gradle" } } is Closure configureClosure
Q2
<< is a shorthand for doLast just to make it more concise. You can use doLast, <<, leftShift - it's all the same. leftShift is overridden - see here
Q3
There's no such method tasks but getTasks, see here. This is how groovy works - if method is a getter () and get can be omitted, so project.getTasks() is equivalent to project.tasks.
Related
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.
When declaring a task in gradle it looks like I can write:
task myTask {
doFirst {
//do something
}
}
I'm wondering how the interpreter knows to run doFirst on the task object?
It would make more sense to me if we had:
it.doFirst {
//do something
}
Because then it would be obvious that doFirst was being called on it, which must be the task object itself being passed into the closure.
How does groovy's syntax allow for my first snippet above to work?
I'm new to groovy but I haven't seen anything in the docs in regards to this
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.
Where does that property it stored in gradle?
subprojects {
println it.class.name // DefaultProject_Decorated
dependencies {
println it.class.name // DefaultDependencyHandler_Decorated
Because it should not be the default it property of the closure. Or it should?
I think it something like def it = this or maybe I am wrong?
In groovy closures, it is the default parameter passed to the Closure.
So:
def friendly = { "Hello $it" }
assert friendly('tim') == 'Hello tim'
So in the above cases, Gradle passes the object that the closure is helping to configure into the closure itself.
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]
}
}