안드로이드 build.gradle.kts에서 Flavor 나눠진 경우 KSP 빌드 폴더를 flavor 별 sourceSets 설정 방법
개인 광고 영역
Android app 배포 시 다양한 형태의 빌드를 위해 Flavor를 제공하고 있다.
이 문서에 빌드 변형 구성을 통한 설명이 잘 나와있다. Gradle뿐만 아니라 kotlin kts도 가이드하고 있다.
이 글에서는 Kotlin Symbol Processing을 활용하여 자동으로 생성된 build 결과물의 경로를 flavor 별로 어떤 식으로 추가하는 게 좋을지 소개합니다.
이 글에서 알아볼 내용
- Flavor 별 sourceSets 구성 방법
- debug/release와 flavor 조합
- 이 글에서는 KSP 사용 방법에 대해서는 설명하지 않습니다.
- KSP에서 만들어진 결과물과 flavor에 따른 폴더 지정 방법을 설명합니다.
Flavor와 buildTypes
Flavor를 나눌 때는 크게 flavor 이름 + debug 또는 flavor 이름 + release로 나눌 수 있습니다.
모두 명시할 필요는 없고, buildTypes
와 productFlavors
를 이용해 나누게 됩니다.
android {
...
buildTypes { // 1
getByName("debug"){...}
getByName("release"){...}
}
// Specifies one flavor dimension.
flavorDimensions += "version"
productFlavors { // 2
create("demo") {
dimension = "version"
applicationIdSuffix = ".demo"
}
create("full") {
dimension = "version"
applicationIdSuffix = ".full"
}
}
}
- debug와 release를 정의합니다. 보통 기본 프로젝트 생성 시 함께 나오는데 여기서는
debug
release
- productFlavors는 build 해야 할 종류를 나누는 작업을 할 수 있는데, 여기서는
demo
full
buildTypes과 productFlavors의 조합은?
위와 같이 조합하면 총 4개가 노출됩니다.
- Debug 빌드 2개
demoDebug
fullDebug
- Release 빌드 2개
demoRelease
fullRelease
와 같이 만들어집니다.
demoDebug와 demoRelease로 빌드 하면 기본 package 이름 뒤에 .demo
가 합쳐져 나오게 됩니다.
기본 package : tech.thdev.app
demo 빌드 시 : tech.thdev.app.demo
flavor를 구분하는 방법은 매우 간단합니다. 각각의 추가 설정해야 할 부분들은 문서를 참고해야 하겠지만 기본 flavor 설정은 이와 같습니다.
sourceSets 정의
Kotlin Symbol Processing API를 활용하여 빌드 한 결과물을 flavor마다 추가하는 방법을 소개합니다.
안드로이드에서 KSP를 통해 빌드 된 결과물을 debug와 release 폴더를 바라보도록 설정해 줘야 하는데, 아래와 같이 추가할 수 있다고 설명되어 있는 글들이 있습니다.
android {
buildTypes {
getByName("debug") {
sourceSets {
getByName("main") {
kotlin.srcDir("build/generated/ksp/debug/kotlin")
}
}
}
getByName("release") {
sourceSets {
getByName("main") {
kotlin.srcDir("build/generated/ksp/release/kotlin")
}
}
}
}
}
하지만 이 코드는 gradle sync를 다시 해보면 아래와 같은 결과를 볼 수 있습니다.
debug 추가 후
source=[src/main/java, src/main/kotlin, build/generated/ksp/debug/kotlin]
release 추가 후
source=[src/main/java, src/main/kotlin, build/generated/ksp/debug/kotlin, build/generated/ksp/release/kotlin]
결국 debug를 빌드 했다가 flavor를 release로 변경한다면 release에서도 debug와 release 폴더 모두를 바라보게 되고, 거꾸로 바꿔도 결과물이 존재하니 둘다 바라보게 됩니다.
이와 함께 아래와 같은 오류가 발생합니다.
KSPOutput.kt: (3, 8): Redeclaration: KSPOutput
결국 위와 같은 코드로는 작업 중 Build vaiants 이동이 잦을 경우 매번 clean build와 함께 rebuild 해야 정상 동작함을 알 수 있습니다.
flavor 별 sourceSets 처리하기
flavor를 구분하고 난 다음엔 더 많은 폴더를 바라보아야 하는데 위에서 나눈 demo
, full
을 기준으로 한다면 KSP도 자동으로 최대 4개의 디렉토리 경로가 생기게 됩니다.
당연히 자동으로 만들어진 코드이고, 4개를 모두 바라볼 수 있으므로 모두 충돌 나서 빌드가 불가능해지게 됩니다.
- build/generated/ksp/demoDebug/kotlin
- build/generated/ksp/demoRelease/kotlin
- build/generated/ksp/fullDebug/kotlin
- build/generated/ksp/fullRelease/kotlin
flavor 이동 및 재빌드 시에 위와 같이 최대 4개의 경로가 새로 생기고, 동일한 파일도 생성됩니다.
이를 flavor 설정에 따라 하나의 폴더만 바라보도록 source 경로 설정이 필요합니다.
buildTypes과 productFlavors를 설정한 build.gradle.kts
에 아래와 같이 추가합니다.
// demo에 대한 flavor 설정
sourceSets.create("demoDebug") {
kotlin.srcDir("build/generated/ksp/demoDebug/kotlin")
println("kotlin $kotlin")
}
sourceSets.create("demoRelease") {
kotlin.srcDir("build/generated/ksp/demoRelease/kotlin")
println("kotlin $kotlin")
}
// full에 대한 flavor 경로 설정
sourceSets.create("fullDebug") {
kotlin.srcDir("build/generated/ksp/fullDebug/kotlin")
println("kotlin $kotlin")
}
sourceSets.create("fullRelease") {
kotlin.srcDir("build/generated/ksp/fullRelease/kotlin")
println("kotlin $kotlin")
}
이와 같이 적용하면 현재 선택한 flavor 기준으로 build/generated/ksp 폴더도 main 폴더가 source code 활성화됨을 볼 수 있습니다.
위에서 buildTypes과 함게 findName()을 통해 sourceSets 한 것과 비교해 println 결과물을 확인해 보면 아래와 같습니다.
// demoDebug 인 경우
source=[src/demoDebug/java, src/demoDebug/kotlin, build/generated/ksp/demoDebug/kotlin]
// demoRelease 인 경우
source=[src/demoRelease/java, src/demoRelease/kotlin, build/generated/ksp/demoRelease/kotlin]
// fullDebug 인 경우
source=[src/fullDebug/java, src/fullDebug/kotlin, build/generated/ksp/fullDebug/kotlin]
// fullRelease 인 경우
source=[src/fullRelease/java, src/fullRelease/kotlin, build/generated/ksp/fullRelease/kotlin]
build.gradle.kts flavor 쉽게 관리하기?
kts로 이동하면서 kotlin 문법을 활용할 수 있습니다. listOf()
를 활용해 flavor와 sourceSets을 좀 더 쉽게 관리할 수 있는데, 내부 설정 내용이 크게 다르지 않을 때 유용합니다.
val flavorList = listOf("demo", "full")
// Specifies one flavor dimension.
flavorDimensions += "deploy"
productFlavors {
flavorList.forEach {
create(it) {
applicationIdSuffix = ".$it"
}
}
}
// sourceSets
flavorList.forEach {
val debug = "${it}Debug"
sourceSets.create(debug) {
kotlin.srcDir("build/generated/ksp/$debug/kotlin")
println("kotlin $kotlin")
}
val release = "${it}Release"
sourceSets.create(release) {
kotlin.srcDir("build/generated/ksp/$release/kotlin")
println("kotlin $kotlin")
}
}
이와 같이 줄일 수 있는데, kotlin property를 활용해 경로의 debug/release만 다른 부분을 한 번 더 수정한다면
val String.kspSourceSet: String
get() = "build/generated/ksp/$this/kotlin"
와 할 수 있습니다.
마무리
이와 같이 buildTypes과 productFlavors 설정에 따라 KSP에서 바라보는 폴더를 지정하는 방법을 살펴보았습니다.
sourceSets에서 create를 활용한 이유는 sourceSets.all {}
을 통해 제공하는 flavor
이름을 확인해 보면 조금 늦은 타이밍에 buildTypes + productFlavors를 합쳐 코드를 생성함을 알 수 있습니다.
결국 딜레이 되어 합성 결과를 알 수 있으니 getByName 대신 create를 활용해 처리하게 됩니다.
KSP는 추후 글을 통해 정리해 보려 합니다.
Comments