From f057517e998020f3ef18e251e8c2f4f27ff41394 Mon Sep 17 00:00:00 2001 From: johnsonlee Date: Mon, 25 Apr 2022 02:26:17 +0800 Subject: [PATCH] Add `booster-task-graph` for task graph visualization --- .../didiglobal/booster/cha/graph/CallGraph.kt | 2 +- booster-task-graph/README.md | 16 +++++ booster-task-graph/build.gradle | 12 ++++ .../task/graph/TaskGraphVariantProcessor.kt | 61 +++++++++++++++++++ .../didiglobal/booster/task/graph/TaskNode.kt | 9 +++ settings.gradle | 1 + 6 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 booster-task-graph/README.md create mode 100644 booster-task-graph/build.gradle create mode 100644 booster-task-graph/src/main/kotlin/com/didiglobal/booster/task/graph/TaskGraphVariantProcessor.kt create mode 100644 booster-task-graph/src/main/kotlin/com/didiglobal/booster/task/graph/TaskNode.kt diff --git a/booster-cha/src/main/kotlin/com/didiglobal/booster/cha/graph/CallGraph.kt b/booster-cha/src/main/kotlin/com/didiglobal/booster/cha/graph/CallGraph.kt index cf3632056..b17d18292 100644 --- a/booster-cha/src/main/kotlin/com/didiglobal/booster/cha/graph/CallGraph.kt +++ b/booster-cha/src/main/kotlin/com/didiglobal/booster/cha/graph/CallGraph.kt @@ -50,7 +50,7 @@ class CallGraph private constructor(private val edges: Map>, val open class Node internal constructor(val type: String, val name: String, val desc: String, val args: String) { constructor(type: String, name: String, desc: String) - : this(type, name, desc, desc.substring(desc.indexOf('(') + 1, desc.lastIndexOf(')'))) + : this(type, name, desc, desc.substring(desc.indexOf('(') + 1, desc.lastIndexOf(')').takeIf { it > -1 } ?: desc.length)) override fun equals(other: Any?) = when { other === this -> true diff --git a/booster-task-graph/README.md b/booster-task-graph/README.md new file mode 100644 index 000000000..3520e94b7 --- /dev/null +++ b/booster-task-graph/README.md @@ -0,0 +1,16 @@ +# booster-task-graph + +Generate task graph in [dot](https://graphviz.org/), please make sure you have installed the [dot command line](https://graphviz.org/doc/info/command.html). + +## Getting Started + +Executing tasks and then find the `*.dot` and `*.dot.png` files under `${rootProject}/build` directory, for example, if I run the following command line + +```bash +./gradlew assembleDebug --dry-run +``` + +Then, the following files will be generated under `${rootProject}/build`: + +* `assembleDebug.dot` +* `assembleDebug.dot.png` diff --git a/booster-task-graph/build.gradle b/booster-task-graph/build.gradle new file mode 100644 index 000000000..e4f1c0891 --- /dev/null +++ b/booster-task-graph/build.gradle @@ -0,0 +1,12 @@ +apply from: "$rootDir/gradle/booster.gradle" + +dependencies { + kapt "com.google.auto.service:auto-service:1.0" + implementation project(':booster-api') + implementation project(':booster-cha') + implementation project(':booster-command') + compileOnly 'com.android.tools.build:gradle:4.0.0' + compileOnly 'com.android.tools.build:builder:4.0.0' + testCompileOnly 'com.android.tools.build:gradle:4.0.0' + testCompileOnly 'com.android.tools.build:builder:4.0.0' +} diff --git a/booster-task-graph/src/main/kotlin/com/didiglobal/booster/task/graph/TaskGraphVariantProcessor.kt b/booster-task-graph/src/main/kotlin/com/didiglobal/booster/task/graph/TaskGraphVariantProcessor.kt new file mode 100644 index 000000000..cc7e5ffd1 --- /dev/null +++ b/booster-task-graph/src/main/kotlin/com/didiglobal/booster/task/graph/TaskGraphVariantProcessor.kt @@ -0,0 +1,61 @@ +package com.didiglobal.booster.task.graph + +import com.android.build.gradle.api.BaseVariant +import com.didiglobal.booster.cha.graph.CallGraph +import com.didiglobal.booster.cha.graph.dot.DotGraph +import com.didiglobal.booster.command.CommandService +import com.didiglobal.booster.gradle.project +import com.didiglobal.booster.kotlinx.OS +import com.didiglobal.booster.kotlinx.execute +import com.didiglobal.booster.kotlinx.file +import com.didiglobal.booster.task.spi.VariantProcessor +import com.google.auto.service.AutoService +import java.io.BufferedReader +import java.io.File +import java.util.concurrent.atomic.AtomicBoolean + +private val DOT = "dot${OS.executableSuffix}" + +@AutoService(VariantProcessor::class) +class TaskGraphVariantProcessor : VariantProcessor { + + private var generating = AtomicBoolean(false) + + override fun process(variant: BaseVariant) { + generating.compareAndSet(false, true).takeIf { it } ?: return + + val project = variant.project + + project.gradle.taskGraph.whenReady { + val taskNames = project.gradle.startParameter.taskNames + val dot = project.rootProject.buildDir.file("${taskNames.joinToString("-")}.dot") + val title = "./gradlew ${taskNames.joinToString(" ")}" + val graph = project.gradle.taskGraph.allTasks.map { task -> + task.taskDependencies.getDependencies(task).map { dep -> + dep to task + } + }.flatten().map { (dep, task) -> + CallGraph.Edge(TaskNode(dep.path), TaskNode(task.path)) + }.fold(CallGraph.Builder().setTitle(title)) { builder, edge -> + builder.addEdge(edge) + builder + }.build() + + // write dot file + dot.writeText(DotGraph.DIGRAPH.render(graph).toString()) + + // convert dot to png + CommandService.fromPath(DOT).location.file.let(::File).takeIf(File::exists)?.let { + val cmdline = "${it.canonicalPath} -Tpng -O ${dot.canonicalPath}" + project.logger.info(cmdline) + cmdline.execute() + }?.let { p -> + p.waitFor() + if (p.exitValue() != 0) { + project.logger.error(p.errorStream.bufferedReader().use(BufferedReader::readText)) + } + } ?: project.logger.warn("Command `${DOT}` not found") + } + } + +} \ No newline at end of file diff --git a/booster-task-graph/src/main/kotlin/com/didiglobal/booster/task/graph/TaskNode.kt b/booster-task-graph/src/main/kotlin/com/didiglobal/booster/task/graph/TaskNode.kt new file mode 100644 index 000000000..2e2412cae --- /dev/null +++ b/booster-task-graph/src/main/kotlin/com/didiglobal/booster/task/graph/TaskNode.kt @@ -0,0 +1,9 @@ +package com.didiglobal.booster.task.graph + +import com.didiglobal.booster.cha.graph.CallGraph + +data class TaskNode(val path: String) : CallGraph.Node("", path, "") { + + override fun toPrettyString(): String = path + +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 0201aa84a..317884f11 100644 --- a/settings.gradle +++ b/settings.gradle @@ -32,6 +32,7 @@ include ':booster-task-compression' include ':booster-task-compression-cwebp' include ':booster-task-compression-pngquant' include ':booster-task-compression-processed-res' +include ':booster-task-graph' include ':booster-task-list-artifact' include ':booster-task-list-permission' include ':booster-task-list-shared-library'