Skip to content

Commit b0d1817

Browse files
committed
[BOOK-121] feat: 프로젝트 모듈 의존성 그래프 생성 스크립트 추가
1 parent f29412d commit b0d1817

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,7 @@ allprojects {
4141
}
4242
}
4343
}
44+
45+
apply {
46+
from("gradle/projectDependencyGraph.gradle")
47+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
tasks.register('projectDependencyGraph') {
2+
doLast {
3+
def dotFileName = 'project.dot'
4+
def dot = new File(rootProject.rootDir, dotFileName)
5+
dot.parentFile.mkdirs()
6+
dot.delete()
7+
8+
dot << 'digraph {\n'
9+
dot << " graph [label=\"${rootProject.name}\\n \",labelloc=t,fontsize=30,ranksep=1.4];\n"
10+
dot << ' node [style=filled, fillcolor="#bbbbbb"];\n'
11+
dot << ' rankdir=TB;\n'
12+
13+
def rootProjects = []
14+
def queue = [rootProject]
15+
while (!queue.isEmpty()) {
16+
def project = queue.remove(0)
17+
rootProjects.add(project)
18+
queue.addAll(project.childProjects.values())
19+
}
20+
21+
def projects = new LinkedHashSet<Project>()
22+
def dependencies = new LinkedHashMap<Tuple2<Project, Project>, List<String>>()
23+
def multiplatformProjects = []
24+
def jsProjects = []
25+
def androidProjects = []
26+
def androidLibraryProjects = []
27+
def androidDynamicFeatureProjects = []
28+
def javaProjects = []
29+
30+
queue = [rootProject]
31+
while (!queue.isEmpty()) {
32+
def project = queue.remove(0)
33+
queue.addAll(project.childProjects.values())
34+
35+
if (project.plugins.hasPlugin('org.jetbrains.kotlin.multiplatform')) {
36+
multiplatformProjects.add(project)
37+
}
38+
if (project.plugins.hasPlugin('kotlin2js')) {
39+
jsProjects.add(project)
40+
}
41+
if (project.plugins.hasPlugin('com.android.application')) {
42+
androidProjects.add(project)
43+
}
44+
if (project.plugins.hasPlugin('com.android.library')) {
45+
androidLibraryProjects.add(project)
46+
}
47+
if (project.plugins.hasPlugin('com.android.dynamic-core')) {
48+
androidDynamicFeatureProjects.add(project)
49+
}
50+
if (project.plugins.hasPlugin('java-library') || project.plugins.hasPlugin('java')) {
51+
javaProjects.add(project)
52+
}
53+
54+
project.configurations.configureEach { config ->
55+
if (config.name.toLowerCase().contains("test")) return
56+
config.dependencies
57+
.withType(ProjectDependency)
58+
.collect { it.dependencyProject }
59+
.each { dependency ->
60+
projects.add(project)
61+
projects.add(dependency)
62+
rootProjects.remove(dependency)
63+
64+
def graphKey = new Tuple2<Project, Project>(project, dependency)
65+
def traits = dependencies.computeIfAbsent(graphKey) { new ArrayList<String>() }
66+
67+
if (config.name.toLowerCase().endsWith('implementation')) {
68+
traits.add('style=dotted')
69+
}
70+
}
71+
}
72+
}
73+
74+
projects = projects.sort { it.path }
75+
76+
dot << '\n # Projects\n\n'
77+
for (project in projects) {
78+
def traits = []
79+
80+
if (rootProjects.contains(project)) {
81+
traits.add('shape=box')
82+
}
83+
84+
if (multiplatformProjects.contains(project)) {
85+
traits.add('fillcolor="#ffd2b3"')
86+
} else if (jsProjects.contains(project)) {
87+
traits.add('fillcolor="#ffffba"')
88+
} else if (androidProjects.contains(project)) {
89+
traits.add('fillcolor="#baffc9"')
90+
} else if (androidLibraryProjects.contains(project)) {
91+
traits.add('fillcolor="#81D4FA"')
92+
} else if (androidDynamicFeatureProjects.contains(project)) {
93+
traits.add('fillcolor="#c9baff"')
94+
} else if (javaProjects.contains(project)) {
95+
traits.add('fillcolor="#ffb3ba"')
96+
} else {
97+
traits.add('fillcolor="#eeeeee"')
98+
}
99+
100+
dot << " \"${project.path}\" [${traits.join(", ")}];\n"
101+
}
102+
103+
dot << '\n {rank = same;'
104+
for (project in projects) {
105+
if (rootProjects.contains(project)) {
106+
dot << " \"${project.path}\";"
107+
}
108+
}
109+
dot << '}\n'
110+
111+
dot << '\n # Dependencies\n\n'
112+
dependencies.forEach { key, traits ->
113+
dot << " \"${key.first.path}\" -> \"${key.second.path}\""
114+
if (!traits.isEmpty()) {
115+
dot << " [${traits.join(", ")}]"
116+
}
117+
dot << '\n'
118+
}
119+
120+
dot << '}\n'
121+
122+
def p = "dot -Tpng -O ${dotFileName}".execute([], dot.parentFile)
123+
p.waitFor()
124+
if (p.exitValue() != 0) {
125+
throw new RuntimeException(p.errorStream.text)
126+
}
127+
dot.delete()
128+
129+
println("Project module dependency graph created at ${dot.absolutePath}.png")
130+
}
131+
}

0 commit comments

Comments
 (0)