Gradle 7 (Groovy 3) - method in a closure executed/called immediately - gradle

I am updating Gradle from 5.6.3 to 7.5.1. I have a gradle custom plugin that accepts a closure. And in my main application's builg.gradle I am calling that custom plugin's task and assigning value to that closure.
main project -
task(type: MyCustomTask, 'custom') {
myClosure = {
filesMatching('**/my.yaml') {
filter { it.replace('${test}', 'TEST') }
}
}
...
}
custom plugin -
#Input
#Optional
Closure myClosure = { }
private void copyFiles() {
println "--------------------------Copying"
project.copy {
from "${project.projectDir}/src/main/resources"
into "${buildDir}"
final cl = myClosure.clone()
cl.delegate = delegate
cl()
}
}
#TaskAction
def custom() {
...
copyFiles()
...
}
This is working fine with gradle 5 (groovy 2.5), but with gradle 7 (groovy 3) I am getting
Execution failed for task 'custom'.
Error while evaluating property 'myClosure' of task 'custom'
Could not find method filesMatching() for arguments [**/my.yaml, build_46x3acd2klzyjg008csx3dlg4$_run_closure1$_closure2$_closure5$_closure6#15088f00] on task 'custom' of type com.custom.gradle.plugin.tasks.TestTask.
Any suggestion here to fix the issue? Thank you!

Related

Methods for a gradle file accesses another gradle file

I have two gradle files:
first file:
def sendMessage(String appName,String versionCode){
println("${appName}---${versionCode}")
}
second file:
afterEvaluate {
android.applicationVariants.each { variant ->
String variantName = variant.name.capitalize()
def task = tasks.create("apkUploadPGY${variantName}")
task.dependsOn("resguard${variantName}")
task.doLast {
//in this how can i use sendMessage function
}
}
}
I want to use the method defined in the first file in the second file. How do I do this?
You can do it in the following way:
build.gradle:
apply from: 'first.gradle'
task whatever {
doLast {
sendMessage("lol", "v1")
}
}
first.gradle:
ext.sendMessage = { String appName, String versionCode ->
println("${appName}--${versionCode}")
}
You also need to change sendMessage declaration. It should be defined as a groovy closure and stored within ext - this is how "methods" are used in gradle.

refer to Gradle task which is defined later in build

Our build has some generic logic to create tasks from configuration data. There are also several Gradle files on whose execution order I do not want to depend.
Now I need to add a task dependency to a task without knowing if that task will be defined before my part of the build script runs (at configuration time) or after my part.
[Edit: as #canillas writes I can say myTask.dependsOn "otherTask" and "lazily" depend on yet undefined "otherTask", but I need it the other way around.]
Since I can't write tasks["otherTask"].dependsOn myNewTask before "otherTask" is defined, I made the following helper function:
void safelyDoWithTask(String taskName, Closure func) {
def task = tasks.findByName(taskName)
if (task != null) {
func(task)
} else {
project.tasks.whenTaskAdded { task_ ->
if (taskName.equals(task_.getName())) {
func(task_)
}
}
}
}
safelyDoWithTask('otherTask', { it.dependsOn myNewTask })
Now I wonder if there is more canonical way to achieve this?
Consider the following:
// section 1
def dynamicDependencies = [:]
dynamicDependencies['otherTask'] = 'myNewTask'
// section 2
afterEvaluate { project ->
// taskA dependsOn taskB
dynamicDependencies.each { taskA, taskB ->
def task = project.tasks.findByName(taskA)
if (task) { task.dependsOn "${taskB}" }
}
}
// section 3
task myNewTask() {
doLast {
println 'myNewTask !'
}
}
task otherTask() {
doLast {
println 'otherTask !'
}
}
The gist is:
section 1 defines our dependency info in a custom map
section 2 uses the afterEvaluate hook to process the map
because the above is decoupled from the task definitions, we can simply put them in section 3 (or wherever)

How do I create my own configuration block with a Gradle script plugin?

Our company has a Gradle script plugin with a number of tasks in it. For instance, it includes the Jacoco afterEvaluate block from this answer:
def pathsToExclude = ["**/*Example*"]
jacocoTestReport {
afterEvaluate {
classDirectories = files(classDirectories.files.collect {
fileTree(dir: it, exclude: pathsToExclude)
})
}
}
We would like to take that pathsToExclude variable and define that in our build.gradle file and have the rest of the logic in a script plugin (let's call it company-script-plugin.gradle. For instance:
apply from: http://example.com/company-script-plugin.gradle
companyConfiguration {
pathsToExclude = ["**/*Example*"]
}
Our initial thought was to add a task in the build script so that we could get a companyConfiguration
task companyConfiguration {
ext.pathsToExclude = []
}
However, we think that this is a hacky workaround because running the task doesn't do anything. What is the proper way to create my own configuration block?
We'd like it to be as simple as possible, and to be a script plugin (rather than binary plugin) if possible.
Here you've an example how it can be done:
apply plugin: CompanyPlugin
companyConfiguration {
pathsToExclude = ['a', 'b', 'c']
}
class CompanyPlugin implements Plugin<Project> {
void apply(Project p) {
println "Plugin ${getClass().simpleName} applied"
p.extensions.create('companyConfiguration', CompanyConfigurationExtension, p)
}
}
class CompanyConfigurationExtension {
List<String> pathsToExclude
CompanyConfigurationExtension(Project p) {
}
}
task printCompanyConfiguration {
doLast {
println "Path to exclide $companyConfiguration.pathsToExclude"
}
}
Also, please have a look at the docs.

Passing varargs as second argument in Groovy in Gradle Tooling API

I am trying to execute the Gradle Tooling API, but I am unable to call the addProgressListner() method as I am experiencing surprising issues during compilation:
buildscript {
repositories {
maven { url 'https://repo.gradle.org/gradle/libs-releases' }
}
dependencies {
classpath "org.gradle:gradle-tooling-api:3.1"
classpath 'org.slf4j:slf4j-simple:1.7.10'
}
}
ext {
GRADLE_PROJECT_HOME = '...'
}
import org.gradle.tooling.*
import org.gradle.tooling.events.OperationType
task testGradleToolingAPI {
doLast {
ProjectConnection projectConnection = GradleConnector.newConnector()
.forProjectDirectory(GRADLE_PROJECT_HOME as File)
.connect()
def operationTypes = [OperationType.TASK] as OperationType[]
println "operationTypes.class: ${operationTypes.class}"
projectConnection.newBuild()
.addProgressListener(new ApolloBuildProgressListener(), operationTypes)
.run()
finally {
projectConnection.close()
}
}
}
class ApolloBuildProgressListener implements ProgressListener {
#Override
void statusChanged(ProgressEvent event) {
println "Progress event: ${event.description}"
}
}
The compilation fails as the vararg is not correctly recognized:
operationTypes.class: class [Lorg.gradle.tooling.events.OperationType;
:testGradleToolingAPI FAILED
FAILURE: Build failed with an exception.
* Where:
Build file '/home/martin/devel/tmp/gradle-tooling-api/build.gradle' line: 36
* What went wrong:
Execution failed for task ':testGradleToolingAPI'.
> No signature of method: org.gradle.tooling.internal.consumer.DefaultBuildLauncher.addProgressListener() is applicable for argument types: (ApolloBuildProgressListener, [Lorg.gradle.tooling.events.OperationType;) values: [ApolloBuildProgressListener#7c2dfa2, [TASK]]
Possible solutions: addProgressListener(org.gradle.tooling.events.ProgressListener, [Lorg.gradle.tooling.events.OperationType;), addProgressListener(org.gradle.tooling.ProgressListener), addProgressListener(org.gradle.tooling.events.ProgressListener), addProgressListener(org.gradle.tooling.events.ProgressListener, java.util.Set)
What am I missing?
I'm guessing that ApolloBuildProgressListener is implementing org.gradle.tooling.ProgressListener when it should be org.gradle.tooling.events.ProgressListener. Try this:
class ApolloBuildProgressListener implements org.gradle.tooling.events.ProgressListener {
...
}
Note that the BuildLauncher has four addProgressListener(...) methods. One accepts a org.gradle.tooling.ProgressListener and the other 3 accept a org.gradle.tooling.events.ProgressListener

Skip variable checking in gradle configuration phase

I have some gradle script where i read some properties file (differen file in different execution configurations) and assign Properties obect to "ext" property in each task.
task provisionMongoDBCopyDockerfile(type: Copy, dependsOn: 'readTestConfiguration') {
from "${projectDir}/deployment/scripts/Dockerfile.mongodb"
into "/tmp/stand/mondodb"
expand(ext.stand)
filteringCharset = 'UTF-8'
}
task readTestConfiguration () {
def props = loadStandProperties('test')
println props
tasks.each {
it.ext.stand = props
println it.ext
}
}
but when i run gradle script i get this error: "Cannot get property 'stand' on extra properties extension as it does not exist" in line with "expand(ext.stand)". How can i solve this problem. I don't want to put all configuration parameters in "gradle.properties" and change it from configuration to configuration.
Consider the following (using Gradle 2.14.1). This effectively sets up a dependency in Configuration phase. Also it uses project.ext versus tasks.ext.
def readTestConfiguration = {
def props = loadStandProperties('test')
println props
project.ext.stand = props
}
def loadStandProperties (def env) {
// use mock data
return ["foo": "bar"]
}
tasks.whenTaskAdded { Task task ->
if (task.name == "provisionMongoDBCopyDockerfile") {
readTestConfiguration()
}
}
task provisionMongoDBCopyDockerfile(type: Copy) {
from "${projectDir}/in"
into "${projectDir}/out"
expand(project.ext.stand)
}

Resources