forked from JetBrains/kotlin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdegrade.init.gradle.kts
155 lines (124 loc) · 5.11 KB
/
degrade.init.gradle.kts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
rootProject {
apply<DegradePlugin>()
}
class DegradePlugin : Plugin<Project> {
override fun apply(target: Project) {
if (target != target.rootProject) return
Degrade(target).register()
}
}
private class Degrade(val rootProject: Project) {
private val scriptDir = rootProject.file("degrade")
private class TaskLog {
val stdout = StringBuilder()
fun clear() {
stdout.clear()
}
}
private fun setupTaskLog(task: Task): TaskLog {
val log = TaskLog()
task.logging.addStandardOutputListener { log.stdout.append(it) }
return log
}
fun register() {
rootProject.gradle.addListener(object : BuildAdapter(), TaskExecutionListener {
val taskToLog = mutableMapOf<Task, TaskLog>()
val allScripts = mutableListOf<String>()
val failedScripts = mutableListOf<String>()
@Synchronized
override fun beforeExecute(task: Task) {
taskToLog.getOrPut(task) { setupTaskLog(task) }.clear()
}
@Synchronized
override fun afterExecute(task: Task, state: TaskState) {
val log = taskToLog[task] ?: return
val script = generateScriptForTask(task, log) ?: return
allScripts += script
if (state.failure != null) {
failedScripts += script
}
}
@Synchronized
override fun buildFinished(result: BuildResult) {
try {
generateAggregateScript("rerun-all.sh", allScripts)
generateAggregateScript("rerun-failed.sh", failedScripts)
} finally {
failedScripts.clear()
allScripts.clear()
}
}
})
}
private fun generateAggregateScript(name: String, scripts: List<String>) = generateScript(name) {
appendLine("""cd "$(dirname "$0")"""")
appendLine()
scripts.forEach {
appendLine("./$it")
}
}
private fun generateScriptForTask(task: Task, taskLog: TaskLog): String? {
val project = task.project
val stdoutLinesIterator = taskLog.stdout.split('\n').iterator()
val commands = parseKotlinNativeCommands { stdoutLinesIterator.takeIf { it.hasNext() }?.next() }
if (commands.isEmpty()) return null
val konanHome = project.properties["konanHome"]
val scriptName = task.path.substring(1).replace(':', '_') + ".sh"
generateScript(scriptName) {
appendLine("""kotlinNativeDist="$konanHome"""")
appendLine()
commands.forEach { command ->
appendLine(""""${"$"}kotlinNativeDist/bin/run_konan" \""")
command.transformedArguments.forEachIndexed { index, argument ->
append(" ")
append(argument)
if (index != command.transformedArguments.lastIndex) {
appendLine(" \\")
}
}
appendLine()
appendLine()
}
}
return scriptName
}
private fun parseKotlinNativeCommands(nextLine: () -> String?): List<KotlinNativeCommand> {
val result = mutableListOf<KotlinNativeCommand>()
while (true) {
val line = nextLine() ?: break
if (line != "Main class = $kotlinNativeEntryPointClass"
&& !line.startsWith("Entry point method = $kotlinNativeEntryPointClass.")) continue
generateSequence(nextLine)
.firstOrNull { it.startsWith("Transformed arguments = ") }
.takeIf { it == "Transformed arguments = [" }
?: continue
val transformedArguments = generateSequence(nextLine)
.takeWhile { it != "]" }
.map { it.trimStart() }
.toList()
result += KotlinNativeCommand(transformedArguments)
}
return result
}
private class KotlinNativeCommand(val transformedArguments: List<String>)
private companion object {
const val kotlinNativeEntryPointClass = "org.jetbrains.kotlin.cli.utilities.MainKt"
// appendLine is not available in Kotlin stdlib shipped with older Gradle versions;
// Copied here:
/** Appends a line feed character (`\n`) to this Appendable. */
private fun Appendable.appendLine(): Appendable = append('\n')
/** Appends value to the given Appendable and a line feed character (`\n`) after it. */
private fun Appendable.appendLine(value: CharSequence?): Appendable = append(value).appendLine()
}
private fun generateScript(name: String, generateBody: Appendable.() -> Unit) {
scriptDir.mkdirs()
val file = File(scriptDir, name)
file.bufferedWriter().use { writer ->
writer.appendLine("#!/bin/sh")
writer.appendLine("set -e")
writer.appendLine()
writer.generateBody()
}
file.setExecutable(true)
}
}