Skip to content

Commit c68605f

Browse files
dependency graph
1 parent decc9fd commit c68605f

3 files changed

Lines changed: 280 additions & 118 deletions

File tree

build.gradle.kts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,12 @@ allprojects {
7575
}
7676
}
7777

78-
apply(from = file("gradle/projectDependencyGraph.gradle"))
78+
val projectDependencyGraph by tasks.registering(ProjectDependencyGraphTask::class) {
79+
doLast {
80+
copy {
81+
from(rootProject.buildDir.resolve("reports/dependency-graph/project.dot.png"))
82+
into(rootProject.projectDir)
83+
}
84+
}
85+
}
86+
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import org.gradle.api.DefaultTask
2+
import org.gradle.api.Project
3+
import org.gradle.api.artifacts.ProjectDependency
4+
import org.gradle.api.tasks.TaskAction
5+
import java.util.Locale
6+
7+
@Suppress("unused")
8+
open class ProjectDependencyGraphTask : DefaultTask() {
9+
@TaskAction
10+
fun run() {
11+
val dot = project.rootDir.resolve("gradle/dependency-graph/project.dot")
12+
dot.parentFile.mkdirs()
13+
dot.delete()
14+
15+
dot.appendText(
16+
"""
17+
|digraph {
18+
| graph [label="${project.rootProject.name}\n ",labelloc=t,fontsize=30,ranksep=1.4];
19+
| node [style=filled, fillcolor="#bbbbbb"];
20+
| rankdir=TB;
21+
|
22+
""".trimMargin()
23+
)
24+
25+
val rootProjects = mutableListOf<Project>()
26+
val queue = mutableListOf(project.rootProject)
27+
while (queue.isNotEmpty()) {
28+
val project = queue.removeAt(0)
29+
rootProjects.add(project)
30+
queue.addAll(project.childProjects.values)
31+
}
32+
33+
val projects = LinkedHashSet<Project>()
34+
val dependencies = LinkedHashMap<Pair<Project, Project>, MutableList<String>>()
35+
val multiplatformProjects = mutableListOf<Project>()
36+
val jsProjects = mutableListOf<Project>()
37+
val androidProjects = mutableListOf<Project>()
38+
val javaProjects = mutableListOf<Project>()
39+
val rankAndroid = mutableListOf<Project>()
40+
val rankDomain = mutableListOf<Project>()
41+
val rankRepository = mutableListOf<Project>()
42+
43+
queue.clear()
44+
queue.add(project.rootProject)
45+
while (queue.isNotEmpty()) {
46+
val project = queue.removeAt(0)
47+
queue.addAll(project.childProjects.values)
48+
49+
if (project.plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")) {
50+
multiplatformProjects.add(project)
51+
}
52+
if (project.plugins.hasPlugin("kotlin2js")) {
53+
jsProjects.add(project)
54+
}
55+
if (project.plugins.hasPlugin("com.android.library") || project.plugins.hasPlugin("com.android.application")) {
56+
androidProjects.add(project)
57+
}
58+
if (project.plugins.hasPlugin("java-library") || project.plugins.hasPlugin("java")) {
59+
javaProjects.add(project)
60+
}
61+
62+
if (!project.path.startsWith(":core:") && project.path.endsWith(":android")) {
63+
rankAndroid.add(project)
64+
}
65+
if (!project.path.startsWith(":core:") && project.path.endsWith(":repository")) {
66+
rankRepository.add(project)
67+
}
68+
69+
project.configurations.all {
70+
getDependencies()
71+
.filterIsInstance<ProjectDependency>()
72+
.filter { project != it.dependencyProject }
73+
.map { it.dependencyProject }
74+
.forEach { dependency ->
75+
projects.add(project)
76+
projects.add(dependency)
77+
rootProjects.remove(dependency)
78+
79+
val graphKey = Pair(project, dependency)
80+
val traits = dependencies.computeIfAbsent(graphKey) { mutableListOf() }
81+
82+
if (name.toLowerCase(Locale.getDefault()).endsWith("implementation")) {
83+
traits.add("style=dotted")
84+
}
85+
}
86+
}
87+
}
88+
89+
projects.sortedBy { it.path }.also {
90+
projects.clear()
91+
projects.addAll(it)
92+
}
93+
94+
dot.appendText("\n # Projects\n\n")
95+
for (project in projects) {
96+
val traits = mutableListOf<String>()
97+
98+
if (rootProjects.contains(project)) {
99+
traits.add("shape=box")
100+
}
101+
102+
if (multiplatformProjects.contains(project)) {
103+
if (androidProjects.contains(project)) {
104+
traits.add("fillcolor=\"#f7ffad\"")
105+
} else {
106+
traits.add("fillcolor=\"#ffd2b3\"")
107+
}
108+
} else if (jsProjects.contains(project)) {
109+
traits.add("fillcolor=\"#ffffba\"")
110+
} else if (androidProjects.contains(project)) {
111+
traits.add("fillcolor=\"#baffc9\"")
112+
} else if (javaProjects.contains(project)) {
113+
traits.add("fillcolor=\"#ffb3ba\"")
114+
} else {
115+
traits.add("fillcolor=\"#eeeeee\"")
116+
}
117+
118+
dot.appendText(" \"${project.path}\" [${traits.joinToString(", ")}];\n")
119+
}
120+
121+
dot.appendText("\n {rank = same;")
122+
for (project in projects) {
123+
if (rootProjects.contains(project)) {
124+
dot.appendText(" \"${project.path}\";")
125+
}
126+
}
127+
dot.appendText("}\n")
128+
129+
for (sameRank in listOf(rankAndroid, rankDomain, rankRepository)) {
130+
dot.appendText("\n {rank = same;")
131+
for (project in sameRank) {
132+
dot.appendText(" \"${project.path}\";")
133+
}
134+
dot.appendText("}\n")
135+
}
136+
137+
dot.appendText("\n # Dependencies\n\n")
138+
dependencies.forEach { (key, traits) ->
139+
dot.appendText(" \"${key.first.path}\" -> \"${key.second.path}\"")
140+
if (traits.isNotEmpty()) {
141+
dot.appendText(" [${traits.joinToString(", ")}]")
142+
}
143+
dot.appendText("\n")
144+
}
145+
146+
dot.appendText("}\n")
147+
148+
project.exec {
149+
commandLine = listOf("sh", "-c", "cd \"${dot.parentFile}\"; dot -Tpng -O project.dot")
150+
}
151+
println("Project module dependency graph created at ${dot.absolutePath}.png")
152+
}
153+
}
154+

0 commit comments

Comments
 (0)