Extract common methods from Gradle build script
Gradlebuild.gradleGradle Problem Overview
I have a Gradle build script (build.gradle
), in which I created some tasks. These tasks consist mostly of method calls. The called methods are also in the build script.
Now, here's the situation:
I am creating a fair amount of build scripts, which contain different tasks, but utilise the same methods from the original script. Thus, I would like to extract these "common methods" in some way, so I can easily re-use them instead of copying them for each new script I create.
If Gradle were PHP, something like the following would be ideal:
//script content
...
require("common-methods.gradle");
...
//more script content
But of course, that isn't possible. Or is it?
Anyway, how can I achieve this result? What is the best possible method to do this? I've read through the Gradle documentation already, but I can't seem to determine which method will be the easiest and best suited for this.
Thanks in advance!
UPDATE:
I've managed to extract the methods in another file
(using apply from: 'common-methods.gradle'
),
so the structure is as follows:
parent/
/build.gradle // The original build script
/common-methods.gradle // The extracted methods
/gradle.properties // Properties used by the build script
After executing a task from build.gradle
, I've bumped into a new problem: apparently, methods don't get recognized when they're in common-methods.gradle
.
Any ideas on how to fix that?
Gradle Solutions
Solution 1 - Gradle
Building on Peter's answer, this is how I export my methods:
Content of helpers/common-methods.gradle
:
// Define methods as usual
def commonMethod1(param) {
return true
}
def commonMethod2(param) {
return true
}
// Export methods by turning them into closures
ext {
commonMethod1 = this.&commonMethod1
otherNameForMethod2 = this.&commonMethod2
}
And this is how I use those methods in another script:
// Use double-quotes, otherwise $ won't work
apply from: "$rootDir/helpers/common-methods.gradle"
// You can also use URLs
//apply from: "https://bitbucket.org/mb/build_scripts/raw/master/common-methods.gradle"
task myBuildTask {
def myVar = commonMethod1("parameter1")
otherNameForMethod2(myVar)
}
Here's more on converting methods to closures in Groovy.
Solution 2 - Gradle
It isn't possible to share methods, but you can share extra properties containing a closure, which boils down to the same thing. For example, declare ext.foo = { ... }
in common-methods.gradle
, use apply from:
to apply the script, and then call the closure with foo()
.
Solution 3 - Gradle
Using the Kotlin DSL it works like this:
build.gradle.kts:
apply {
from("external.gradle.kts")
}
val foo = extra["foo"] as () -> Unit
foo()
external.gradle.kts:
extra["foo"] = fun() {
println("Hello world!")
}
Solution 4 - Gradle
Another approach for Kotlin DSL could be:
my-plugin.gradle.kts
extra["sum"] = { x: Int, y: Int -> x + y }
settings.gradle.kts
@Suppress("unchecked_cast", "nothing_to_inline")
inline fun <T> uncheckedCast(target: Any?): T = target as T
apply("my-plugin.gradle.kts")
val sum = uncheckedCast<(Int, Int) -> Int>(extra["sum"])
println(sum(1, 2))
Solution 5 - Gradle
I would suggest a slight adjustment to Matthias Braun's answer, in that instead of writing the same method-name twice and still have it clear and consise, why not simply do the following:
ext.commonMethod1 = (param) -> {
return true
} as Closure<boolean>
The usage of the as
-operator simply tells one explicitly,
that this function will return a value of boolean
-type.
Because after all, this still is Groovy goodness. Neat huh?