com.autonomousapps.tasks.ComputeDominatorTreeTask.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dependency-analysis-gradle-plugin Show documentation
Show all versions of dependency-analysis-gradle-plugin Show documentation
Analyzes dependency usage in Android and JVM projects
@file:Suppress("SpellCheckingInspection")
package com.autonomousapps.tasks
import com.autonomousapps.TASK_GROUP_DEP_INTERNAL
import com.autonomousapps.graph.DominanceTree
import com.autonomousapps.graph.DominanceTreeWriter
import com.autonomousapps.graph.Graphs.reachableNodes
import com.autonomousapps.internal.graph.GraphWriter
import com.autonomousapps.internal.utils.FileUtils
import com.autonomousapps.internal.utils.fromJson
import com.autonomousapps.internal.utils.fromJsonSet
import com.autonomousapps.internal.utils.getAndDelete
import com.autonomousapps.model.*
import com.autonomousapps.model.PhysicalArtifact
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import java.io.File
@CacheableTask
abstract class ComputeDominatorTreeTask : DefaultTask() {
init {
group = TASK_GROUP_DEP_INTERNAL
description = "Computes a dominator view of the dependency graph"
}
@get:Input
abstract val projectPath: Property
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val physicalArtifacts: RegularFileProperty
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val graphView: RegularFileProperty
@get:OutputFile
abstract val outputTxt: RegularFileProperty
@get:OutputFile
abstract val outputDot: RegularFileProperty
@TaskAction fun action() {
val outputTxt = outputTxt.getAndDelete()
val outputDot = outputDot.getAndDelete()
val artifactMap = physicalArtifacts.fromJsonSet().associate { (coord, file) ->
coord to file
}
val graphView = graphView.fromJson()
val project = ProjectCoordinates(projectPath.get(), GradleVariantIdentification(setOf("ROOT"), emptyMap()), ":")
val tree = DominanceTree(graphView.graph, project)
val nodeWriter = BySize(
files = artifactMap,
tree = tree,
root = project
)
val writer: DominanceTreeWriter = DominanceTreeWriter(
root = project,
tree = tree,
nodeWriter = nodeWriter,
)
outputTxt.writeText(writer.string)
outputDot.writeText(GraphWriter.toDot(tree.dominanceGraph))
}
private class BySize(
private val files: Map,
private val tree: DominanceTree,
root: Coordinates
) : DominanceTreeWriter.NodeWriter {
private val sizes = mutableMapOf()
private val reachableNodes = mutableMapOf>()
private fun getSize(node: Coordinates): Long = sizes.computeIfAbsent(node) { treeSizeOf(it) }
private fun reachableNodes(node: Coordinates) = reachableNodes.computeIfAbsent(node) {
tree.dominanceGraph.reachableNodes(node, excludeSelf = false)
}
private fun treeSizeOf(node: Coordinates): Long = reachableNodes(node)
.mapNotNull { files[it] }
.sumOf { it.length() }
// Get the scale (bytes, KB, MB, ...) for printing.
private val scale = reachableNodes(root)
.mapNotNull { files[it] }
.sumOf { it.length() }
.let { FileUtils.getScale(it) }
private val comparator = Comparator { left, right ->
// nb: right.compareTo(left) is intentional. Sorted descending.
getSize(right).compareTo(getSize(left))
}
override fun comparator(): Comparator = comparator
override fun toString(node: Coordinates): String {
val builder = StringBuilder()
var printedTotalSize = false
val subs = reachableNodes(node)
if ((subs - node).isNotEmpty()) {
val totalSize = subs
.mapNotNull { files[it] }
.sumOf { it.length() }
printedTotalSize = true
builder.append(FileUtils.byteCountToDisplaySize(totalSize, scale)).append(' ')
}
files[node]
?.length()
?.let {
if (printedTotalSize) builder.append('(')
builder.append(FileUtils.byteCountToDisplaySize(it, scale))
if (printedTotalSize) builder.append(')')
builder.append(' ')
}
val preferredCoordinatesNotation = if (node is IncludedBuildCoordinates) {
node.resolvedProject
} else {
node
}
builder.append(preferredCoordinatesNotation.gav())
return builder.toString()
}
}
}