107

As part of my project, I need to read files from a directory and do some operations all these in build script. For each file, the operation is the same(reading some SQL queries and execute it). I think its a repetitive task and better to write inside a method. Since I'm new to Gradle, I don't know how it should be. Please help.

1

8 Answers 8

140

One approach given below:

ext.myMethod = { param1, param2 ->
    // Method body here
}

Note that this gets created for the project scope, ie. globally available for the project, which can be invoked as follows anywhere in the build script using myMethod(p1, p2) which is equivalent to project.myMethod(p1, p2)

The method can be defined under different scopes as well, such as within tasks:

task myTask {
    ext.myMethod = { param1, param2 ->
        // Method body here
    }

    doLast {
        myMethod(p1, p2) // This will resolve 'myMethod' defined in task
    }
}
4
  • One more doubt. Can I mark a method local to the task?. ext.myMethod will mark it as global.
    – Tomin
    Commented Jan 7, 2015 at 8:01
  • 5
    When we use ext, the scope is restricted to where it is defined, ie. if it is defined under a task, it is local to the task. The way it works is through entities (like project, task etc.) that implement ExtensionAware. This adds an ExtraPropertiesExtension which is configured through the ext directive. Commented Jan 7, 2015 at 8:42
  • So that means I can use the same method name in different tasks without any conflict. ie; ext.myMethod in two or more tasks should work.
    – Tomin
    Commented Jan 7, 2015 at 10:12
  • 1
    When accessing from a sub-project, can be accessed as rootProject.ext.myMethod(p1, p2)
    – Philippe
    Commented Mar 9, 2018 at 21:27
48

If you have a method in a different .gradle file, then defining it as ext.method() makes it accessible project wide so that it can be called by simply invoking it as method(). For example:

versioning.gradle:

// ext makes method callable project wide
ext.getVersionName = { ->
    try {
        def branchout = new ByteArrayOutputStream()
        exec {
            commandLine 'git', 'rev-parse', '--abbrev-ref', 'HEAD'
            standardOutput = branchout
        }
        def branch = branchout.toString().trim()

        if (branch.equals("master")) {
            def stdout = new ByteArrayOutputStream()
            exec {
                commandLine 'git', 'describe', '--tags'
                standardOutput = stdout
            }
            return stdout.toString().trim()
        } else {
            return branch;
        }
    }
    catch (ignored) {
        return null;
    }
}

build.gradle:

apply from: "$project.rootDir/versioning.gradle"
task showVersion << {
    // Use inherited method
    println 'VersionName: ' + getVersionName()
}

Without the ext.method() definition using ext., the method will only be available within the .gradle file it is declared in. This is the same with properties.

31

You can define methods in the following way:

// Define an extra property
ext.srcDirName = 'src/java'

// Define a method
def getSrcDir(project) {
    return project.file(srcDirName)
}

You can find more details in gradle documentation Chapter 62. Organizing Build Logic

2
  • 1
    I tried this, however the method I am creating is executed on every task I am running, for example when trying to run ./gradlew clean - I see the method is being executed and this is not what I want - do you know what might be the problem? Commented Feb 28, 2020 at 22:25
  • @user1002065 hard to tell without seeing your setup, if you could please share (i.e. as a gist), then I could try to help Commented Mar 2, 2020 at 14:00
15

An example with a root object containing methods.

hg.gradle file:

ext.hg = [

    cloneOrPull: { source, dest, branch ->
        if (!dest.isDirectory())
            hg.clone(source, dest, branch)
        else
            hg.pull(dest)
        hg.update(dest, branch)
    },

    clone: { source, dest, branch ->
        dest.mkdirs()
        exec {
            commandLine 'hg', 'clone', '--noupdate', source, dest.absolutePath
        }
    },

    pull: { dest ->
        exec {
            workingDir dest.absolutePath
            commandLine 'hg', 'pull'
        }
    },

]

build.gradle file

apply from: 'hg.gradle'

hg.clone('path/to/repo')
0
11

Somehow, maybe because it's five years since the OP, but none of the

ext.someMethod = { foo ->
   methodBody
}

approaches are working for me. Instead, a simple function definition seems to be getting the job done in my gradle file:

def retrieveEnvvar(String envvar_name) {
    if ( System.getenv(envvar_name) == "" ) {
        throw new InvalidUserDataException("\n\n\nPlease specify environment variable ${envvar_name}\n")
    } else {
        return System.getenv(envvar_name)
    }       
}

And I call it elsewhere in my script with no prefix, ie retrieveEnvvar("APP_PASSWORD")

This is 2020 so I'm using Gradle 6.1.1.

3
  • 1
    I'm also on gradle 6.1.1, ex.someMethod = {} works fine. Just that I could not use param names while calling the lambda.
    – rpattabi
    Commented Sep 2, 2020 at 7:19
  • 1
    Sure, that works, but your method only exists within that script. So when you need the same functionality in two places, it unfortunately doesn't help.
    – GhostCat
    Commented Mar 9, 2021 at 10:44
  • How do you reach this function inside the settings.gradle file? Commented Oct 23, 2023 at 12:08
2

@ether_joe the top-voted answer by @InvisibleArrow above does work however you must define the method you call before you call it - i.e. earlier in the build.gradle file.

You can see an example here. I have used this approach with Gradle 6.5 and it works.

1

In my react-native in build.gradle

def func_abc(y){return "abc"+y;}

then

def x = func_abc("y");

If you want to check:

throw new GradleException("x="+x);

or

println "x="+x;
1

With Kotlin DSL (build.gradle.kts) you can define regular functions and use them.
It doesn't matter whether you define your function before the call site or after it.

println(generateString())

fun generateString(): String {
    return "Black Forest"
}

tasks.create("MyTask") {
    println(generateString())
}

If you want to import and use a function from another script, see this answer and this answer.

Not the answer you're looking for? Browse other questions tagged or ask your own question.