where is _Decorated suffix in ::class names from? - gradle

I am learning gradle + Kotlin DSL by trying to understand all the "magic" objects (implicit receivers) it creates:
// hello, world build.gradle.kts
tasks.register("hello") {
doLast {
println("Hello world! ${tasks::class} ${this::class}")
}
}
I am curious what is the object class of the (predefined)tasks container or the Taskthat is implicitly created (receiver of doLast I guess?); I get:
Hello world! class org.gradle.api.internal.tasks.DefaultTaskContainer_Decorated class org.gradle.api.DefaultTask_Decorated
I shouldn't be surprised by the *.internal class name but where is DefaultTask_Decorated from? The public api lists *.api.DefaultTask only.

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.

How to access extra properties defined in build.gradle in a Groovy class?

I am defining an extra property in build.gradle:
ext {
GRGIT_TARGET_REPO = ...
}
I have a groovy class under buildSrc/src/main/groovy
class Utils {
def test() {
println GRGIT_TARGET_REPO
}
}
I have another gradle file which can access GRGIT_TARGET_REPO just fine. But if it calls the function in the class:
Utils utils = new Utils()
utils.test()
On calling the above function, I get the following error:
No such property: GRGIT_TARGET_REPO for class: Utils
Is there a way to access project/extra properties in Groovy classes?
I believe you would need to send the gradle project object into your Utils class to accomplish what you want. In other words:
class Utils {
def project
def test() {
println(project.ext.GRGIT_TARGET_REPO)
}
}
and
def utils = new Utils(project: project)
utils.test()
in your build.gradle file.
Every gradle build file has a project instance as its delegate which means that you can call all methods in the project class directly from the build file code. The example here is that the above project access calls the getProject method on the project object.
For extra properties, there is an "extra properties" section in the above groovy/javadoc for the Project object.

gradle task method syntax in build.gradle

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.

Understand Gradle-Groovy semantics

I am having a very hard time understanding the semantics of gradle scripts w.r.t how they are seen in groovy.
1) What does the following snippet mean?
task copy(type: Copy) {
into "target"
with baseSpec
}
As I understand it, it seems to me that task is instantiated with a named parameter "type" and it's value "Copy". I have no idea what is "into", "with". Are they parameters of the task class? BTW, Is task a class or interface?
2) What is a "script block"? Is it a closure?
3) What is an "Action"? Are they also closures or objects of interface instantiated with anonymous class?
Basically, I am lost how to put all of this together as a plain groovy ?
Groovy is a powerful language for building DSL (Domain Specific Language). Gradle use this as many others libraries.
It's based on several properties of Groovy
Parenthesis are optionals
fun("myparameter")
fun "myparameter"
You can have named parameters on a method
fun prop:'value', otherprop:'othervalue'
fun([prop:'value', otherprop:'othervalue'])
If the last parameters of a method is a closure, it can be written outside the method call
fun(prop:'value') {
//..closure call
}
fun([prop:'value'], { /*closure*/ })
You can get/set any property or invoke any method on a groovy object : you can add behavior dynamically, through missingMethod, missingProperty, getProperty or setProperty, ..
object.somefun "42"
object.missingMethod("somefun", ["42"])
In a closure, you have a special object, called delegate. it can be setted at runtime, and any non-local property or method invocation can be delegated to this delegate
def fun = { copy "this_file" }
def fun = { delegate.copy("this_file") }
See this documentation or the Builder pattern
with this properties, your script can be written (it's not really true because of AST transformation..) :
task(copy([type: Copy], { it ->
delegate.into("target")
delegate.with(baseSpec)
}))
delegate is an object which implement missingMethod, and generate objects based on the method call and the context.
a more complexe script :
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
}
is equivalent to :
buildscript({ it ->
delegate.repositories({delegate.mavenCentral()})
delegate.dependencies({delegate.classpath([group:'commons-codec', name:'commons-codec', version:'1.2'])})
})

Multiple ways of using custom Gradle plugins in build.gradle

With reference to the Gradle docs section 59.2 I have created a simple plugin to illustrate various (seemingly working) ways to use a custom Gradle plugin's DSL exposed via plugin extensions. For example the following plugin definition and class
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
// Add the 'greeting' extension object
project.extensions.create("greeting", GreetingPluginExtension)
// Add a task that uses the configuration
project.task('hello') << {
println project.greeting.message
}
}
}
class GreetingPluginExtension {
def String message = 'Hello from GreetingPlugin'
}
can be called in four ways
greeting.message 'Hi from Gradle with dot'
greeting.message = 'Hi from Gradle with dot and assigment'
greeting { message 'Hi from Gradle with block' }
greeting { message = 'Hi from Gradle with block with assignment' }
what is the correct and recommended way? Are there implications of using one way over another? In that simple example they all appear to work
As Opal said, all 4 ways are good, depending on your requirements in readability. If you have more things to configure than just the message, then the configuration block using a closure may be more convenient.
There's also a difference between using the assignment operator and omitting it: when using the assignment operator, you are explicitly setting a property, while omitting it means calling a method with that name. In that case, I prefer using the assignment operator.
You can have a look here: http://groovy-lang.org/style-guide.html#_getters_and_setters

Resources