// Copyright (C) 2024 Gradle, Inc.
//
// Licensed under the Creative Commons Attribution-Noncommercial-ShareAlike 4.0 International License.;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://creativecommons.org/licenses/by-nc-sa/4.0/
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

[[groovy_build_script_primer]]
= A Groovy Build Script Primer

Ideally, a Groovy build script looks mostly like configuration: setting some properties of the project, configuring dependencies, declaring tasks, and so on. That configuration is based on Groovy language constructs. This primer aims to explain what those constructs are and — most importantly — how they relate to Gradle's API documentation.

[[groovy:project_object]]
== The `Project` object

As Groovy is an object-oriented language based on Java, its properties and methods apply to objects. In some cases, the object is implicit — particularly at the top level of a build script, i.e. not nested inside a `{}` block.

Consider this fragment of build script, which contains an unqualified property and block:

[source,groovy]
----
version = '1.0.0.GA'

configurations {
    ...
}
----

Both `version` and `configurations {}` are part of link:{groovyDslPath}/org.gradle.api.Project.html[org.gradle.api.Project].

This example reflects how every Groovy build script is backed by an implicit instance of `Project`. If you see an unqualified element and you don't know where it's defined, always check the `Project` API documentation to see if that's where it's coming from.

[CAUTION]
--
Avoid using https://groovy-lang.org/metaprogramming.html#_metaclasses[Groovy MetaClass] programming techniques in your build scripts. Gradle provides its own API for adding <<writing_build_scripts_intermediate.adoc#sec:extra_properties,dynamic runtime properties>>.

Use of Groovy-specific metaprogramming can cause builds to retain large amounts of memory between builds that will eventually cause the Gradle daemon to run out-of-memory.
--

[[groovy:properties]]
== Properties

[source,groovy]
<obj>.<name>                // Get a property value
<obj>.<name> = <value>      // Set a property to a new value
"$<name>"                   // Embed a property value in a string
"${<obj>.<name>}"           // Same as previous (embedded value)

[source,groovy]
.Examples
----
version = '1.0.1'
myCopyTask.description = 'Copies some files'

file("$projectDir/src")
println "Destination: ${myCopyTask.destinationDir}"
----

A property represents some state of an object. The presence of an `=` sign is a clear indicator that you're looking at a property. Otherwise, a qualified name — it begins with `<obj>.` — without any other decoration is also a property.

If the name is unqualified, then it may be one of the following:

 * A task instance with that name.
 * A property on link:{groovyDslPath}/org.gradle.api.Project.html[Project].
 * An <<writing_build_scripts_intermediate.adoc#sec:extra_properties,extra property>> defined elsewhere in the project.
 * A property of an implicit object within a <<#groovy:blocks,block>>.
 * A <<#groovy:local_variables,local variable>> defined earlier in the build script.

Note that plugins can add their own properties to the `Project` object. The link:{groovyDslPath}/[API documentation] lists all the properties added by core plugins. If you're struggling to find where a property comes from, check the documentation for the plugins that the build uses.

TIP: When referencing a project property in your build script that is added by a non-core plugin, consider prefixing it with `project.` — it's clear then that the property belongs to the project object.

=== Properties in the API documentation

The link:{groovyDslPath}/[Groovy DSL reference] shows properties as they are used in your build scripts, but the Javadocs only display methods. That's because properties are implemented as methods behind the scenes:

 * A property can be _read_ if there is a method named `get<PropertyName>` with zero arguments that returns the same type as the property.
 * A property can be _modified_ if there is a method named `set<PropertyName>` with one argument that has the same type as the property and a return type of `void`.

Note that property names usually start with a lower-case letter, but that letter is upper case in the method names. So the getter method `getProjectVersion()` corresponds to the property `projectVersion`. This convention does not apply when the name begins with at least two upper-case letters, in which case there is not change in case. For example, `getRAM()` corresponds to the property `RAM`.

[source,groovy]
.Examples
----
project.getVersion()
project.version

project.setVersion('1.0.1')
project.version = '1.0.1'
----


[[groovy:methods]]
== Methods

[source,groovy]
<obj>.<name>()              // Method call with no arguments
<obj>.<name>(<arg>, <arg>)  // Method call with multiple arguments
<obj>.<name> <arg>, <arg>   // Method call with multiple args (no parentheses)

[source,groovy]
.Examples
----
myCopyTask.include '**/*.xml', '**/*.properties'

ext.resourceSpec = copySpec()   // `copySpec()` comes from `Project`

file('src/main/java')
println 'Hello, World!'
----

A method represents some behavior of an object, although Gradle often uses methods to configure the state of objects as well. Methods are identifiable by their arguments or empty parentheses. Note that parentheses are sometimes required, such as when a method has zero arguments, so you may find it simplest to always use parentheses.

NOTE: Gradle has a convention whereby if a method has the same name as a collection-based property, then the method _appends_ its values to that collection.

[[groovy:blocks]]
== Blocks

Blocks are also <<#groovy:methods,methods>>, just with specific types for the last argument.

[source,groovy]
----
<obj>.<name> {
     ...
}

<obj>.<name>(<arg>, <arg>) {
     ...
}
----

[source,groovy]
.Examples
----
plugins {
    id 'java-library'
}

configurations {
    assets
}

sourceSets {
    main {
        java {
            srcDirs = ['src']
        }
    }
}

dependencies {
    implementation project(':util')
}
----

Blocks are a mechanism for configuring multiple aspects of a build element in one go. They also provide a way to nest configuration, leading to a form of structured data.

There are two important aspects of blocks that you should understand:

 1. They are implemented as methods with specific signatures.
 2. They can change the target ("delegate") of unqualified methods and properties.

Both are based on Groovy language features and we explain them in the following sections.

=== Block method signatures

You can easily identify a method as the implementation behind a block by its signature, or more specifically, its argument types. If a method corresponds to a block:

 * It must have at least one argument.
 * The _last_ argument must be of type https://docs.groovy-lang.org/latest/html/gapi/groovy/lang/Closure.html[`groovy.lang.Closure`] or link:{javadocPath}/org/gradle/api/Action.html[org.gradle.api.Action].

For example, link:{groovyDslPath}/org.gradle.api.Project.html#org.gradle.api.Project:copy(org.gradle.api.Action)[Project.copy(Action)] matches these requirements, so you can use the syntax:

[source,groovy]
copy {
    into layout.buildDirectory.dir("tmp")
    from 'custom-resources'
}

That leads to the question of how `into()` and `from()` work. They're clearly methods, but where would you find them in the API documentation? The answer comes from understanding object _delegation_.

=== Delegation

The <<#groovy:properties,section on properties>> lists where unqualified properties might be found. One common place is on the `Project` object. But there is an alternative source for those unqualified properties and methods inside a block: the block's _delegate object_.

To help explain this concept, consider the last example from the previous section:

[source,groovy]
copy {
    into layout.buildDirectory.dir("tmp")
    from 'custom-resources'
}

All the methods and properties in this example are unqualified. You can easily find `copy()` and `layout` in the link:{groovyDslPath}/org.gradle.api.Project.html[`Project` API documentation], but what about `into()` and `from()`? These are resolved against the delegate of the `copy {}` block. What is the type of that delegate? You'll need to link:{groovyDslPath}/org.gradle.api.Project.html#org.gradle.api.Project:copy(org.gradle.api.Action)[check the API documentation for that].

There are two ways to determine the delegate type, depending on the signature of the block method:

 * For `Action` arguments, look at the type's parameter.
+
In the example above, the method signature is `copy(Action<? super CopySpec>)` and it's the bit inside the angle brackets that tells you the delegate type — link:{javadocPath}/org/gradle/api/file/CopySpec.html[CopySpec] in this case.
 * For `Closure` arguments, the documentation will explicitly say in the description what type is being configured or what type the delegate it (different terminology for the same thing).

Hence you can find both link:{javadocPath}/org/gradle/api/file/CopySpec.html#into-java.lang.Object-[into()] and link:{javadocPath}/org/gradle/api/file/CopySpec.html#from-java.lang.Object++...++-[from()] on `CopySpec`. You might even notice that both of those methods have variants that take an `Action` as their last argument, which means you can use block syntax with them.

All new Gradle APIs declare an `Action` argument type rather than `Closure`, which makes it very easy to pick out the delegate type. Even older APIs have an `Action` variant in addition to the old `Closure` one.

[[groovy:local_variables]]
== Local variables

[source,groovy]
def <name> = <value>        // Untyped variable
<type> <name> = <value>     // Typed variable

[source,groovy]
.Examples
----
def i = 1
String errorMsg = 'Failed, because reasons'
----

Local variables are a Groovy construct — unlike <<writing_build_scripts_intermediate.adoc#sec:extra_properties,extra properties>> — that can be used to share values within a build script.

[CAUTION]
====
Avoid using local variables in the root of the project, i.e. as pseudo project properties. They cannot be read outside of the build script and Gradle has no knowledge of them.

Within a narrower context — such as configuring a task — local variables can occasionally be helpful.
====
