Skip to content

Instantly share code, notes, and snippets.

@arunkumar9t2
Last active July 21, 2023 15:43
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arunkumar9t2/60da93e4866b59dc440d8d291d2d5655 to your computer and use it in GitHub Desktop.
Save arunkumar9t2/60da93e4866b59dc440d8d291d2d5655 to your computer and use it in GitHub Desktop.
Gradle task to find reverse dependencies of a module
import com.google.common.graph.Graphs
import com.google.common.graph.MutableValueGraph
import com.google.common.graph.ValueGraphBuilder
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.options.Option
abstract class DependantModulesTask : DefaultTask() {
@Option(
option = "project-path",
description = "The project path to find dependencies for"
)
@Input
lateinit var projectPath: String
@TaskAction
fun action() {
val rootProject = project.rootProject
val buildGraph: MutableValueGraph<Project, Configuration> =
ValueGraphBuilder.directed()
.allowsSelfLoops(false)
.expectedNodeCount(rootProject.subprojects.size)
.build()
rootProject.subprojects.forEach { sourceProject ->
buildGraph.addNode(sourceProject)
sourceProject
.configurations
.flatMap { configuration ->
configuration
.dependencies
.filterIsInstance<ProjectDependency>()
.map { projectDependency -> configuration to projectDependency }
}.forEach { (configuration, projectDependency) ->
if (sourceProject != projectDependency.dependencyProject) {
// Invert the order since we are only interested in reverse dependencies
buildGraph.putEdgeValue(
projectDependency.dependencyProject,
sourceProject,
configuration
)
}
}
}
val projectToFind = rootProject.project(projectPath)
val dependants = Graphs.reachableNodes(buildGraph.asGraph(), projectToFind)
logger.quiet(buildString {
appendLine()
append("${projectToFind.path}'s dependants :")
appendLine()
append(
dependants
.filter { it != projectToFind }
.joinToString(separator = "\n") { it.path }
)
})
logger.quiet("----------\n")
val subgraph = Graphs.inducedSubgraph(buildGraph, dependants)
subgraph
.edges()
.forEach { endpointPair ->
val configuration = buildGraph
.edgeValue(endpointPair)
.map { it.name }
.orElse("")
val sourceProject = endpointPair.nodeV().path
val dependencyProject = endpointPair.nodeU().path
logger.quiet(
"""|$sourceProject
| $configuration(project("$dependencyProject"))"""
.trimMargin()
)
}
}
}
@arunkumar9t2
Copy link
Author

Example usage when registered as dependantModules task.

./gradlew dependantModules --project-path :sample-kotlin-lib

Screenshot 2022-05-25 at 6 45 34 AM

@handstandsam
Copy link

Beautiful! Maybe the output could look like:

:sample-android-flavor
  implementation(project(":sample-kotlin-lib"))
  
:sample-android
  implementation(project(":sample-kotlin-lib"))

This is super nice, thank you!

@arunkumar9t2
Copy link
Author

Thanks @handstandsam updated to print like that now

Screenshot 2022-05-26 at 12 49 52 AM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment