Passing varargs as second argument in Groovy in Gradle Tooling API - gradle

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

Related

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

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!

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.

Custom task type not found when importing Gradle plugin

I am facing the following conundrum for which I spent a great deal of time trying to resolve with no success so far. I have a custom Gradle plugin whose job is to start a process and run it in the background.
My code plugin code is the following:
public class TaskSpawnerPlugin implements Plugin<Project> {
void apply(Project project) {
project.task('spawnTask', type: TaskSpawner)
}
}
And this is the task in question:
public class TaskSpawner extends DefaultTask {
#Input
String command
#Input
String ready
#Input
String workDir = '.'
TaskSpawner() {
description = 'Given a Unix like cmd, this will start it and let it run on the background'
}
#TaskAction
public void spawn() {
getLogger().quiet "Attempting to run provided command $command"
if (!(command && ready)) {
throw new GradleException("Please make sure that both the command and ready check are provided!")
}
waitFor(createProcess(workDir, command))
}
private def waitFor(Process process) {
new BufferedReader(new InputStreamReader(process.getInputStream())).withCloseable {
reader ->
def line
while ((line = reader.readLine()) != null) {
getLogger().quiet line
if (line.contains(ready)) {
getLogger().quiet "$command is ready"
break
}
}
}
}
private def static createProcess(String directory, String command) {
new ProcessBuilder(command.split(' '))
.redirectErrorStream(true)
.directory(Paths.get(directory).toFile())
.redirectError(ProcessBuilder.Redirect.INHERIT)
.redirectInput(ProcessBuilder.Redirect.INHERIT)
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
.start()
}
}
The code resides under a package structure that has following package name
fts.gradle
My build script looks like this:
plugins {
id 'java-gradle-plugin'
id 'groovy'
id 'maven-publish'
}
group = 'fts.gradle'
version = '0.3'
repositories {
jcenter()
}
dependencies {
compile gradleApi()
compile localGroovy()
}
gradlePlugin {
plugins {
greetingsPlugin {
id = 'fts.gradle.taskspawn'
implementationClass = 'fts.gradle.TaskSpawnerPlugin'
}
}
}
I build my plugin normally and deploy it on a locally hosted artifactory. My problem revolves around how to import it and use it in a project.
For the time being I do the following:
buildscript {
repositories {
maven { url "<maven_url>" }
}
dependencies {
classpath group: 'fts.gradle', name: 'task-spawner', version: '0.3'
}
}
plugins {
id 'java'
id 'application'
id 'eclipse'
}
apply plugin: 'fts.gradle'
And then I attempt to apply it using the following:
But when attempting to refresh the project this action fails:
* What went wrong:
A problem occurred evaluating project ':integration-tests'.
> Could not get unknown property 'TaskSpawner' for project ':integration-tests' of type org.gradle.api.Project.
I have read the documentation and I have tried all the various ways of creating and importing a plugin as a standalone jar but so far I've unsuccessful.
Can anyone please shed some light here? This has been driving nuts for the past days.
Note, for reference the Gradle version I use it 5.6.2

Gradle 'Unknown Property' : Importing Plugin within my Custom Plugin

I am writing a custom Plugin that has a task which makes HTTP-API Calls.
Hence within my custom plugin's build.gradle, I have included the below plugins tag
plugins {
id 'java-gradle-plugin'
id 'groovy'
id 'maven-publish'
id 'io.github.http-builder-ng.http-plugin' version '0.1.1'
}
The task within my custom-plugin is this
task makeRESTCall() {
onlyIf {
!inputList.empty
}
doLast {
//println 'Successfully made REST Call'
//println inputList
def http = groovyx.net.http.HttpBuilder.configure {
request.uri = 'http://localhost:8080'
request.contentType = 'application/json'
request.uri.path = '/api/v1/validate'
}
http.post {
request.body = inputList.toString()
response.success {resp, json ->
println json
if (!json) {
throw new GradleException("Validation Failed")
}
}
}
}
}
My custom-plugin gets built property and when i include the custom-plugin in another project and when I execute the task makeRESTCall, i get the below exception
Execution failed for task ':api:makeRESTCall'.
Could not get unknown property 'groovyx' for task ':api:makeRESTCall' of type org.gradle.api.DefaultTask.
the http-plugin that I import within my custom-plugin is not getting imported properly in my Project
In your custom plugin, you are using HTTP-Builder-NG library (groovyx.net.http.HttpBuilder class), so you need to configure a dependency to this library in your plugin project:
dependencies {
compile "io.github.http-builder-ng:http-builder-ng-core:1.0.3"
}
To make a quick test you could create the following temporary plugin in the buildSrc directory of the project you want to apply the plugin to:
buildSrc/build.gradle
dependencies {
compile "io.github.http-builder-ng:http-builder-ng-core:1.0.3"
}
repositories {
mavenCentral()
}
buildSrc/src/main/groovy/com/mycompany/MyPlugin.groovy
package com.mycompany
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
// ... your plugin login here, with 'inputList' definition
project.task ('makeRESTCall') {
onlyIf {
!inputList.empty
}
doLast {
//println 'Successfully made REST Call'
println inputList
def http = groovyx.net.http.HttpBuilder.configure{
request.uri = 'http://localhost:8080'
request.contentType = 'application/json'
request.uri.path = '/api/v1/validate'
}
http.post {
request.body = inputList.toString()
response.success {resp, json ->
println json
if (!json) {
throw new GradleException("Validation Failed")
}
}
}
}
}
}
build.gradle
import com.mycompany.MyPlugin
apply plugin: MyPlugin
Note : I don't think you need to apply plugin id "io.github.http-builder-ng.http-plugin" version "0.1.1", unless you are using the HTTPTask that this plugin exposes, which is just a Gradle Task wrapper around groovyx.net.http.HttpBuilder

Show stacktraces only for unexpected exceptions in Gradle

In my build files I always set option to show stacktraces by default
gradle.startParameter.showStacktrace = org.gradle.api.logging.configuration.ShowStacktrace.ALWAYS
This is very useful when unexpected failure happens. However, there are some types of failures and causes that are typical (like compilation failure or code quality violation) and in such cases I don't want stacktrace - just meaningful output on console is enough.
Is there a way to disable showing stacktrace for some certain whitelist of exceptions or tasks?
Here is one way, though it is somewhat involved. The idea is based on the notion of a Custom Logger.
Imagine that we have a Java project with 2 extra tasks:
apply plugin: 'java'
task foo() << {
throw new IllegalStateException("from foo")
}
task bar() << {
throw new GradleException("from bar")
}
Suppose further that we want to suppress stacktraces (or print a condensed version) for IllegalStateException and CompilationFailedException (but not GradleException). To this end, consider the following init.gradle file:
useLogger(new CustomEventLogger())
class CustomEventLogger extends BuildAdapter implements TaskExecutionListener {
def exceptionWhitelist = ["IllegalStateException", "CompilationFailedException"]
public void beforeExecute(Task task) {
println "[$task.name]"
}
public void afterExecute(Task task, TaskState state) {
println()
}
private def getCauses(def throwable) {
def causes = []
def t = throwable
while (t != null) {
causes << t.class.simpleName
t = t.cause
}
causes
}
public void buildFinished(BuildResult result) {
println 'build completed'
def throwable = result.failure
if (throwable != null) {
println "TRACER failed with: " + throwable.class
def causes = getCauses(throwable)
if (!hasCauseInWhitelist(causes)) {
throwable.printStackTrace()
} else {
causes.each { println it }
}
}
}
}
The init.gradle must be specified on the command-line. e.g. gradle -I init.gradle compileJava.
It will print condensed info for the two exceptions specified. (Note that walks the hierarchy of causes to find a match.)
The downside is that this "completely disables Gradle's default output", so there may be further tweaks required, depending on requirements.
For example, if I intentionally put a syntax error in Java:
bash$ gradle -I init.gradle compileJava
[compileJava]
~/src/main/java/net/codetojoy/Employee.java:4: error: class, interface, or enum expected
public clazz Employee {
^
1 error
build completed
TRACER failed with: class org.gradle.internal.exceptions.LocationAwareException
LocationAwareException
TaskExecutionException
CompilationFailedException
then we observe the condensed info for the stacktrace.

Resources