모듈 디펜던시 관계와 그래프를 얻는 플러그인
https://github.com/vanniktech/gradle-dependency-graph-generator-plugin
가끔 실수로 모듈 관계를 이상하게 해놓고 발견 못할 때가 있습니다. 이 플러그인은 Android module 간의 관계를 그래프와 텍스트로 출력해 주는 gradle 플러그인 입니다.
설정
현재 최신버전인 0.8.0 버전에는 문제가 있어, 0.7.0을 사용해야 합니다.
Gradle 3.3 기준
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "com.vanniktech:gradle-dependency-graph-generator-plugin:0.7.0"
}
}
apply plugin: "com.vanniktech.dependency.graph.generator"
Gradle 7 이후 버전 기준
plugins {
id 'com.android.application'
// 아래에 추가
id "com.vanniktech.dependency.graph.generator" version "0.7.0"
}
실행
모든 모듈간 의존관계를 보기 위해서는 아래 커맨드를 실행합니다.
./gradlew generateDependencyGraph
현재 프로젝트내의 모듈간 의존관계를 보기 위해서는 아래 커맨드를 실행합니다.
./gradlew generateProjectDependencyGraph
결과
app/build/reports/dependency-graph : 모든 모듈간의 의존관계
app/build/reports/project-dependency-graph: 해당 프로젝트 내 모듈간의 의존관계
위 폴더에 결과 값이 생성되는데, png 와 svg 파일은 위와같이 이미지로 표시되며, dot 파일은 아래와 같이 텍스트 형태로 의존관계를 표현합니다.
"화살을 맞는 녀석은 누가 쐈는지 모른다" 는 표현 방식에 따라, 화살표가 시작되는 쪽이 화살표를 맞는 쪽에 의존한다는 의미입니다.
":app" -> ":base" ["style"="dotted"]
":app" -> ":moduleA" ["style"="dotted"]
":moduleA" -> ":base" ["style"="dotted"]
응용: 만약 이번 커밋에 영향을 받는 모든 모듈의 이름을 얻고 싶다면
(이전 브랜치는 항상 master에 머지되며, master가 직전 릴리즈라고 가정)
App Gradle, 빌드 타입 부분에 추가:
tasks.whenTaskAdded { task ->
if (task.name.contains("assemble")) {
task.dependsOn affectedAreaTask
}
}
App gradle, 아래쪽 아무곳에나, 다른 영역에 속하지 않는 최상위 부분에 추가
task affectedAreaTask {
doLast {
affectedArea()
}
}
App gradle, 아래쪽 아무곳에나, 다른 영역에 속하지 않는 최상위 부분에 추가
def affectedArea() {
println "Affected area"
// Get dependencies
def commandGenerateDependencyGraph = "./gradlew generateProjectDependencyGraph"
def processGenerateDependencyGraph = commandGenerateDependencyGraph.execute()
processGenerateDependencyGraph.waitFor()
if (processGenerateDependencyGraph.exitValue() != 0) {
println "Error, ${processGenerateDependencyGraph.err.text}"
System.exit(-1)
}
String fileContents = new File("$projectDir/build/reports/project-dependency-graph/project-dependency-graph.dot").getText('UTF-8')
List dependency = fileContents
.split('\n')
.findAll { it.contains('->') }
.collect { it.split('\\[').first().replaceAll("\"", "").replaceAll(":", "") }
def keyDependsOnValueMap = [:]
dependency.forEach {
String[] twoWords = it.split("\\->")
if (twoWords[1].trim() != twoWords[0].trim()) {
keyDependsOnValueMap.multiPut(twoWords[1].trim(), twoWords[0].trim())
}
}
println "keyDependsOnValueMap: $keyDependsOnValueMap"
// Get changed modules
def commandGetDiff = "git diff origin/master --name-only" // replace master to develop, if you want to compare it with develop branch.
def processGetDiff = commandGetDiff.execute()
processGetDiff.waitFor()
if (processGetDiff.exitValue() != 0) {
println "Error, ${processGetDiff.err.text}"
System.exit(-1)
}
def changedModuleSet = [] as Set<String>
def affectedAreas = processGetDiff.in.text.readLines().collect {
def found = it.find("(.*?)[/]") // find first item of split by "/"
if (found != null) {
found = found.replaceAll("/", "")
}
found
}
affectedAreas.collect {
if (it != null) {
changedModuleSet.add(it)
}
}
println "changedModuleSet: $changedModuleSet"
// Calculate affected area
def affectedModules = [] as Set
addFound(keyDependsOnValueMap, affectedModules, changedModuleSet)
println "affectedModules: $affectedModules"
}
private void addFound(Map keyDependsOnValueMap, Set affectedModules, Set found) {
for (module in found) {
affectedModules.add(module)
def nestedFound = keyDependsOnValueMap[module] as Set
addFound(keyDependsOnValueMap, affectedModules, nestedFound)
}
}