Skip to content

Commit

Permalink
feat: generateProjectGraph task.
Browse files Browse the repository at this point in the history
Generates dependency graph for local (project) dependencies.
  • Loading branch information
autonomousapps committed May 2, 2024
1 parent fbdcba0 commit 731bef1
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.autonomousapps.jvm

import com.autonomousapps.jvm.projects.AbiAnnotationsProject

import static com.autonomousapps.utils.Runner.build
import static com.google.common.truth.Truth.assertThat

final class ProjectGraphSpec extends AbstractJvmSpec {

def "can generate project graph (#gradleVersion)"() {
given:
def project = new AbiAnnotationsProject(AbiAnnotationsProject.Target.CLASS)
gradleProject = project.gradleProject
when:
build(gradleVersion, gradleProject.rootDir, ':proj:generateProjectGraphMain')
then:
def compileGraph = gradleProject
.singleArtifact('proj', 'reports/dependency-analysis/main/graph/project/project-compile-classpath.gv')
def runtimeGraph = gradleProject
.singleArtifact('proj', 'reports/dependency-analysis/main/graph/project/project-runtime-classpath.gv')
assertThat(compileGraph.exists()).isTrue()
assertThat(runtimeGraph.exists()).isTrue()
where:
gradleVersion << gradleVersions()
}
}
18 changes: 12 additions & 6 deletions src/main/kotlin/com/autonomousapps/internal/OutputPaths.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
package com.autonomousapps.internal

import org.gradle.api.Project
import org.gradle.api.file.Directory
import org.gradle.api.file.RegularFile
import org.gradle.api.provider.Provider

internal const val ROOT_DIR = "reports/dependency-analysis"

Expand All @@ -13,8 +16,8 @@ internal class OutputPaths(
variantName: String
) {

private fun file(path: String) = project.layout.buildDirectory.file(path)
private fun dir(path: String) = project.layout.buildDirectory.dir(path)
private fun file(path: String): Provider<RegularFile> = project.layout.buildDirectory.file(path)
private fun dir(path: String): Provider<Directory> = project.layout.buildDirectory.dir(path)

private val variantDirectory = "$ROOT_DIR/$variantName"
private val intermediatesDir = "${variantDirectory}/intermediates"
Expand Down Expand Up @@ -60,15 +63,18 @@ internal class OutputPaths(
val runtimeDominatorGraphPath = file("${graphDir}/graph-dominator-runtime.gv")
val compileDominatorJsonPath = file("${graphDir}/graph-dominator.json")
val runtimeDominatorJsonPath = file("${graphDir}/graph-dominator-runtime.json")

val projectGraphDir = dir("$graphDir/project")
}

/**
* Differs from [OutputPaths] in that this is for project-aggregator tasks that don't have variants.
*/
@Suppress("SameParameterValue")
internal class NoVariantOutputPaths(private val project: Project) {

@Suppress("SameParameterValue")
private fun file(path: String) = project.layout.buildDirectory.file(path)
private fun file(path: String): Provider<RegularFile> = project.layout.buildDirectory.file(path)
private fun dir(path: String): Provider<Directory> = project.layout.buildDirectory.dir(path)

val locationsPath = file("$ROOT_DIR/declarations.json")
val resolvedDepsPath = file("$ROOT_DIR/resolved-dependencies-report.txt")
Expand All @@ -90,7 +96,7 @@ internal class NoVariantOutputPaths(private val project: Project) {
*/
internal class RootOutputPaths(private val project: Project) {

private fun file(path: String) = project.layout.buildDirectory.file(path)
private fun file(path: String): Provider<RegularFile> = project.layout.buildDirectory.file(path)

val duplicateDependenciesPath = file("$ROOT_DIR/duplicate-dependencies-report.json")
val buildHealthPath = file("$ROOT_DIR/build-health-report.json")
Expand All @@ -101,7 +107,7 @@ internal class RootOutputPaths(private val project: Project) {
internal class RedundantSubPluginOutputPaths(private val project: Project) {

@Suppress("SameParameterValue")
private fun file(path: String) = project.layout.buildDirectory.file(path)
private fun file(path: String): Provider<RegularFile> = project.layout.buildDirectory.file(path)

/**
* This path doesn't use variants because the task that uses it only ever has one instance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.autonomousapps.internal.utils.toCoordinates
import com.autonomousapps.model.Coordinates
import com.autonomousapps.model.DependencyGraphView
import com.google.common.graph.Graph
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.artifacts.result.ResolvedComponentResult
import org.gradle.api.artifacts.result.ResolvedDependencyResult

Expand All @@ -19,13 +20,16 @@ import org.gradle.api.artifacts.result.ResolvedDependencyResult
internal class GraphViewBuilder(
root: ResolvedComponentResult,
fileCoordinates: Set<Coordinates>,
private val localOnly: Boolean = false,
) {

val graph: Graph<Coordinates>

private val graphBuilder = DependencyGraphView.newGraphBuilder()

private val visited = mutableSetOf<Coordinates>()
private val componentFilter: (ResolvedDependencyResult) -> Boolean = {
!localOnly || it.selected.id is ProjectComponentIdentifier
}

init {
val rootId = root.rootCoordinates()
Expand All @@ -46,8 +50,10 @@ internal class GraphViewBuilder(
}

private fun walk(root: ResolvedComponentResult, rootId: Coordinates) {
root.dependencies
root.dependencies.asSequence()
// Only resolved dependencies
.filterIsInstance<ResolvedDependencyResult>()
.filter(componentFilter)
// AGP adds all runtime dependencies as constraints to the compile classpath, and these show
// up in the resolution result. Filter them out.
.filterNot { it.isConstraint }
Expand Down
10 changes: 10 additions & 0 deletions src/main/kotlin/com/autonomousapps/internal/utils/utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.gradle.api.artifacts.result.DependencyResult
import org.gradle.api.artifacts.result.ResolvedDependencyResult
import org.gradle.api.file.RegularFile
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Provider
import java.io.File
import java.util.*

Expand All @@ -23,6 +24,15 @@ internal fun RegularFileProperty.getAndDelete(): File {
return file
}

/**
* Resolves the file from the provider and deletes its contents, then returns the file.
*/
internal fun Provider<RegularFile>.getAndDelete(): File {
val file = get().asFile
file.delete()
return file
}

/**
* Buffer reads of the nullable RegularFileProperty from disk to the set.
*/
Expand Down
17 changes: 17 additions & 0 deletions src/main/kotlin/com/autonomousapps/subplugin/ProjectPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,23 @@ internal class ProjectPlugin(private val project: Project) {
dependencyUsageReports.add(computeUsagesTask.flatMap { it.output })
androidScoreTask?.let { t -> androidScoreReports.add(t.flatMap { it.output }) }
}

// Generates graph view of local (project) dependencies
tasks.register<ProjectGraphTask>("generateProjectGraph$taskNameSuffix") {
compileClasspath.set(
configurations[dependencyAnalyzer.compileConfigurationName]
.incoming
.resolutionResult
.rootComponent
)
runtimeClasspath.set(
configurations[dependencyAnalyzer.runtimeConfigurationName]
.incoming
.resolutionResult
.rootComponent
)
output.set(outputPaths.projectGraphDir)
}
}

private fun Project.configureAggregationTasks() {
Expand Down
7 changes: 5 additions & 2 deletions src/main/kotlin/com/autonomousapps/tasks/GraphViewTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,19 @@ abstract class GraphViewTask : DefaultTask() {
val outputRuntime = outputRuntime.getAndDelete()
val outputRuntimeDot = outputRuntimeDot.getAndDelete()

val variant = variant.get()
val kind = kind.get()

val compileGraph = GraphViewBuilder(compileClasspathResult.get(), compileClasspathFileCoordinates.get()).graph
val compileGraphView = DependencyGraphView(
variant = Variant(variant.get(), kind.get()),
variant = Variant(variant, kind),
configurationName = compileClasspathName.get(),
graph = compileGraph
)

val runtimeGraph = GraphViewBuilder(runtimeClasspathResult.get(), runtimeClasspathFileCoordinates.get()).graph
val runtimeGraphView = DependencyGraphView(
variant = Variant(variant.get(), kind.get()),
variant = Variant(variant, kind),
configurationName = runtimeClasspathName.get(),
graph = runtimeGraph
)
Expand Down
52 changes: 52 additions & 0 deletions src/main/kotlin/com/autonomousapps/tasks/ProjectGraphTask.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.autonomousapps.tasks

import com.autonomousapps.TASK_GROUP_DEP
import com.autonomousapps.internal.graph.GraphViewBuilder
import com.autonomousapps.internal.graph.GraphWriter
import com.autonomousapps.internal.utils.getAndDelete
import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.result.ResolvedComponentResult
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction

@CacheableTask
abstract class ProjectGraphTask : DefaultTask() {

init {
group = TASK_GROUP_DEP
description = "Generates a graph view of this project's local dependency graph"
}

@get:Input
abstract val compileClasspath: Property<ResolvedComponentResult>

@get:Input
abstract val runtimeClasspath: Property<ResolvedComponentResult>

@get:OutputDirectory
abstract val output: DirectoryProperty

@TaskAction fun action() {
val compileOutput = output.file("project-compile-classpath.gv").getAndDelete()
val runtimeOutput = output.file("project-runtime-classpath.gv").getAndDelete()

val compileGraph = GraphViewBuilder(
root = compileClasspath.get(),
fileCoordinates = emptySet(),
localOnly = true,
).graph

val runtimeGraph = GraphViewBuilder(
root = runtimeClasspath.get(),
fileCoordinates = emptySet(),
localOnly = true,
).graph

compileOutput.writeText(GraphWriter.toDot(compileGraph))
runtimeOutput.writeText(GraphWriter.toDot(runtimeGraph))
}
}

0 comments on commit 731bef1

Please sign in to comment.