Skip to content

Commit 049fdde

Browse files
committed
Resilience for failure to detemine task details
In case of failure in resolving one of the following: - task inputs - task outputs - task description Continue building the task tree. Display the failure reason instead of the resolved detail in the tree.
1 parent 8e3958b commit 049fdde

File tree

6 files changed

+94
-17
lines changed

6 files changed

+94
-17
lines changed

src/main/groovy/com/dorongold/gradle/tasktree/NodeAction.groovy

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.dorongold.gradle.tasktree
22

33
import groovy.transform.TupleConstructor
44
import org.gradle.api.Action
5+
import org.gradle.internal.Try
56
import org.gradle.internal.graph.GraphRenderer
67
import org.gradle.internal.logging.text.StyledTextOutput
78

@@ -47,22 +48,23 @@ class NodeAction implements Action<StyledTextOutput> {
4748
}
4849

4950
if (withInputs) {
50-
printTaskFiles(graphRenderer, taskDetails.fileInputs, "<- ", FailureHeader, Failure)
51+
printTaskFiles(graphRenderer, taskDetails.fileInputs, "<- ", 'inputs', FailureHeader, Failure)
5152
}
5253
if (withOutputs) {
53-
printTaskFiles(graphRenderer, taskDetails.fileOutputs, "-> ", SuccessHeader, Success)
54+
printTaskFiles(graphRenderer, taskDetails.fileOutputs, "-> ", 'outputs', SuccessHeader, Success)
5455
}
5556
}
5657

57-
static void printTaskDescription(GraphRenderer graphRenderer, String description, StyledTextOutput.Style textStyle) {
58+
static void printTaskDescription(GraphRenderer graphRenderer, Try<String> description, StyledTextOutput.Style textStyle) {
5859
graphRenderer.output
5960
.withStyle(textStyle)
60-
.text(" - " + description)
61+
.text(" - " + description.getOrMapFailure { e -> "Error! Failure determining description: [${e}]" })
6162
}
6263

63-
static void printTaskFiles(GraphRenderer graphRenderer, List<String> files, String prefix, StyledTextOutput.Style prefixStyle, StyledTextOutput.Style textStyle) {
64+
static void printTaskFiles(GraphRenderer graphRenderer, Try<List<String>> files, String prefix,
65+
String detailName, StyledTextOutput.Style prefixStyle, StyledTextOutput.Style textStyle) {
6466
graphRenderer.startChildren()
65-
files.eachWithIndex { File file, int i ->
67+
files.getOrMapFailure { e -> ["[Error determining ${detailName}: ${e}]"] }.eachWithIndex { String file, int i ->
6668
graphRenderer.output.println()
6769
graphRenderer.output
6870
.withStyle(Info)

src/main/groovy/com/dorongold/gradle/tasktree/TaskTreeTaskBase.groovy

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//file:noinspection GrMethodMayBeStatic
12
package com.dorongold.gradle.tasktree
23

34
import groovy.transform.Canonical
@@ -11,10 +12,15 @@ import org.gradle.api.tasks.diagnostics.internal.TextReportRenderer
1112
import org.gradle.execution.plan.DefaultExecutionPlan
1213
import org.gradle.execution.plan.Node
1314
import org.gradle.initialization.BuildClientMetaData
15+
import org.gradle.internal.Try
1416
import org.gradle.internal.graph.GraphRenderer
1517
import org.gradle.internal.logging.text.StyledTextOutput
18+
import org.slf4j.Logger
19+
import org.slf4j.LoggerFactory
1620
import utils.TaskGraphUtils
1721

22+
import java.util.concurrent.Callable
23+
1824
import static org.gradle.internal.logging.text.StyledTextOutput.Style.FailureHeader
1925
import static org.gradle.internal.logging.text.StyledTextOutput.Style.Info
2026
import static org.gradle.internal.logging.text.StyledTextOutput.Style.SuccessHeader
@@ -25,6 +31,9 @@ import static org.gradle.internal.logging.text.StyledTextOutput.Style.UserInput
2531
* Date: 10/09/15*/
2632

2733
abstract class TaskTreeTaskBase extends AbstractProjectBasedReportTask<TaskReportModel> {
34+
35+
private static final Logger LOGGER = LoggerFactory.getLogger(TaskTreeTaskBase)
36+
2837
public TextReportRenderer renderer = new TextReportRenderer()
2938

3039
@Internal
@@ -45,6 +54,9 @@ abstract class TaskTreeTaskBase extends AbstractProjectBasedReportTask<TaskRepor
4554
@Internal
4655
GraphRenderer graphRenderer
4756

57+
@Internal
58+
Map<Node, TaskDetails> taskDetailsCache = new HashMap<>()
59+
4860
@Override
4961
protected TextReportRenderer getRenderer() {
5062
return renderer
@@ -59,9 +71,9 @@ abstract class TaskTreeTaskBase extends AbstractProjectBasedReportTask<TaskRepor
5971
@EqualsAndHashCode(includes = ['path'])
6072
static final class TaskDetails {
6173
String path
62-
String description
63-
List<String> fileInputs
64-
List<String> fileOutputs
74+
Try<String> description
75+
Try<List<String>> fileInputs
76+
Try<List<String>> fileOutputs
6577

6678
Collection<TaskDetails> children
6779
}
@@ -88,14 +100,22 @@ abstract class TaskTreeTaskBase extends AbstractProjectBasedReportTask<TaskRepor
88100
}
89101

90102
TaskDetails buildTaskDetails(Node taskNode) {
91-
String path = taskNode.task.getIdentityPath() as String
92-
String description = taskNode.task.description as String
93-
List<String> inputs = taskNode.task.inputs.files as List<String>
94-
List<String> outputs = taskNode.task.outputs.files as List<String>
95-
return new TaskDetails(path,
96-
description,
97-
inputs,
98-
outputs)
103+
taskDetailsCache.computeIfAbsent(taskNode, this::buildTaskDetailsInternal)
104+
}
105+
106+
TaskDetails buildTaskDetailsInternal(Node taskNode) {
107+
LOGGER.info("[task-tree plugin]: building task details for task ${taskNode}")
108+
String taskPath = taskNode.task.getIdentityPath() as String
109+
Try<String> taskDescription = withDescription ? tryBuildDetailForTaskPath(taskPath, 'description',
110+
() -> taskNode.task.description as String) : null
111+
Try<List<String>> taskInputs = withInputs ? tryBuildDetailForTaskPath(taskPath, 'inputs',
112+
() -> taskNode.task.inputs.files.collect { it.toString() }) : null
113+
Try<List<String>> taskOutputs = withOutputs ? tryBuildDetailForTaskPath(taskPath, 'outputs',
114+
() -> taskNode.task.outputs.files.collect { it.toString() }) : null
115+
return new TaskDetails(taskPath,
116+
taskDescription,
117+
taskInputs,
118+
taskOutputs)
99119
}
100120

101121
@Override
@@ -186,4 +206,13 @@ abstract class TaskTreeTaskBase extends AbstractProjectBasedReportTask<TaskRepor
186206

187207
}
188208

209+
static <U> Try<U> tryBuildDetailForTaskPath(String taskPath, String detailName, Callable<U> failable) {
210+
try {
211+
return Try.successful(failable.call())
212+
} catch (Exception e) {
213+
LOGGER.info("[task-tree plugin]: Error determining ${detailName} for task ${taskPath}", e)
214+
return Try.failure(e)
215+
}
216+
}
217+
189218
}

src/test/groovy/com/dorongold/gradle/tasktree/TaskTreeTaskTest.groovy

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,23 @@ class TaskTreeTaskTest extends Specification {
201201
result.output.contains expectedOutputProjectPersonService()
202202
}
203203

204+
@UsesSample('failures/')
205+
def "taskTree with failures determining task inputs"() {
206+
when:
207+
def result = GradleRunner.create()
208+
.withProjectDir(sampleProject.dir)
209+
.withArguments('compileGroovy', 'taskTree', '--with-inputs')
210+
.withPluginClasspath()
211+
.build()
212+
213+
then:
214+
result.output =~ expectedOutputProjectFailures()
215+
216+
result.task(":taskTree").outcome == SUCCESS
217+
result.task(":compileGroovy").outcome == SKIPPED
218+
219+
}
220+
204221

205222
static String expectedOutputAllArgumentsWithoutRepeat(Path projectRoot) {
206223
"""
@@ -605,6 +622,21 @@ Project ':services:personService'
605622
'''.stripIndent()
606623
}
607624

625+
static String expectedOutputProjectFailures() {
626+
$/
627+
------------------------------------------------------------
628+
Root project 'failures'
629+
------------------------------------------------------------
630+
631+
:compileGroovy
632+
<- \[Error determining inputs: org.gradle.api.GradleException: Cannot infer Groovy class path because no Groovy Jar was found on class path: \[.*failures/build/classes/java/main\]\]
633+
\\--- :compileJava
634+
635+
636+
\(\*\) - subtree omitted \(printed previously\)
637+
/$.stripIndent()
638+
}
639+
608640
private void populateBuildFile() {
609641
buildFile << """
610642
plugins {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
plugins {
2+
id 'java'
3+
id 'groovy' // applying the groovy plugin without setting groovy runtime causes groovyCompile to fail resolving inputs: https://docs.gradle.org/current/userguide/groovy_plugin.html#sec:automatic_configuration_of_groovyclasspath
4+
id 'com.dorongold.task-tree'
5+
}
6+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rootProject.name = "failures"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package samples.failures.src.main.groovy.org.gradle.sample
2+
3+
class Main {
4+
static void main(String[] args) {
5+
System.out.println("Hello, world!")
6+
}
7+
}

0 commit comments

Comments
 (0)