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
// Copyright (c) 2024. Tony Robalik.
// SPDX-License-Identifier: Apache-2.0
@file:Suppress("SpellCheckingInspection")
package com.autonomousapps.tasks
import com.autonomousapps.TASK_GROUP_DEP
import com.autonomousapps.graph.DependencySizeTree
import com.autonomousapps.graph.DominanceTree
import com.autonomousapps.graph.DominanceTreeDataWriter
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.bufferWriteParameterizedJson
import com.autonomousapps.internal.utils.fromJson
import com.autonomousapps.internal.utils.fromJsonSet
import com.autonomousapps.internal.utils.getAndDelete
import com.autonomousapps.model.*
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
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
@get:OutputFile
abstract val outputJson: RegularFileProperty
@TaskAction fun action() {
compute(
projectPath = projectPath,
outputTxt = outputTxt,
outputDot = outputDot,
outputJson = outputJson,
physicalArtifacts = physicalArtifacts,
graphView = graphView
)
}
private class BySize(
private val files: Map,
private val tree: DominanceTree,
root: Coordinates,
) : DominanceTreeWriter.NodeWriter {
private val sizes = mutableMapOf()
private val reachableNodes = mutableMapOf>()
override fun getTreeSize(node: Coordinates): Long = sizes.computeIfAbsent(node) { treeSizeOf(it) }
override fun getSize(node: Coordinates): Long? = files[node]?.length()
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.
getTreeSize(right).compareTo(getTreeSize(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()
}
}
private companion object {
@Suppress("NAME_SHADOWING")
fun compute(
projectPath: Property,
outputTxt: RegularFileProperty,
outputDot: RegularFileProperty,
outputJson: RegularFileProperty,
physicalArtifacts: RegularFileProperty,
graphView: RegularFileProperty,
) {
val outputTxt = outputTxt.getAndDelete()
val outputDot = outputDot.getAndDelete()
val outputJson = outputJson.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,
)
val dataWriter = DominanceTreeDataWriter(
root = project,
tree = tree,
nodeWriter = nodeWriter,
)
outputTxt.writeText(writer.string)
outputDot.writeText(GraphWriter.toDot(tree.dominanceGraph))
outputJson.bufferWriteParameterizedJson, String>(
dataWriter.sizeTree.map { it.identifier } // we only really care about the identitfiers
)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy