[서론]
멀티 모듈을 사용 할 때, build.gradle파일이 여러개 생긴다.
이 파일들에서 공통적으로 사용할 변수를 쓴다거나, dependency들의 버전을 통일 하고자 할 때
사용할 수 있는 방법들에 대해서 알아보고자 한다.
따로 글을 분리할 수 도 있겠지만,
한 글에 각각 장단점을 적어보려하니
장문의 글이 되어버렸다.
[내 개발환경]
- gradle에서 Kotlin DSL (kts파일)을 사용하였음.
// 만약 groovy파일을 사용하면,
- ext(extra) 문법 달라짐.
- buildSrc는 사용하지 못하는것으로 알고 있음. (근데 또, gradle문서 보니까 groovy도 되는거 아닌가 싶은데, buildSrc에 java소스셋 만들면 되는거 아닌가..?, buildSrc기능이 변수따로 빼서 관리하는거 말고 커스텀 플러그인? 만드는게 있는거 같은데..잘모르겠다.)
[목차]
1. ext(extra) 이용하기
2. buildSrc 이용하기
3. libs.versions.toml 파일 이용하기(version catalog)
--------------
4. 업데이트할 dependency가 있는지 확인하는법 (아직 안해봐서 해보고 더 작성할 예정)
ext, buildSrc, toml뭘 쓰던지, kts파일에서는 dependency버전을 참조하여 작성하면, 업데이트가 있는지 린트가 보이지 않는것으로 알고 있음.
-> 업데이트할 dependency가 있는지, 써드파티 플러그인을 설치하여 확인해야함.
kts파일인데 해당 기능이 필요한분은 아래 링크를 참고바람.
https://www.charlezz.com/?p=46075
1. ext (extra) 이용하기
build.gradle (project level)에서
properies를 선언하여,
다른 모듈 build.gradle파일에서도
동일한 값을 적용시킬 수 있도록 관리 할 수 있다.
// 구글 문서 (Configure project-wide properties)
// gradle문서
https://docs.gradle.org/current/userguide/writing_build_scripts.html#sec:extra_properties
// 참고 할만한 스택오버플로
(검색 키워드 android gradle kts kotlin dsl ext extra)
-----------------------------------
ext (extra) 이용하기
문법이 좀 다양하게 올 수 있는듯한데,
내가 작성해둔게 제대로 된게 아닐 수도 있다.
일단 해본거 snippet을 올림.
(kts파일임, groovy에서는 문법이 다르니 주의)
// build.gradle.kts (project level)
// build.gradle.kts (project level)
...
buildscript {
val majorVersion = 0
val minorVersion = 0
val patchVersion = 1
val versionName = "$majorVersion.$minorVersion.$patchVersion"
val versionCode = 1
extra.apply {
set("compileSdk", 32)
set("targetSdk", 32)
set("minSdk", 23)
set("majorVersion", majorVersion)
set("minorVersion", minorVersion)
set("patchVersion", patchVersion)
set("versionName", versionName)
set("versionCode", versionCode)
set("core-ktxVersion", "1.7.0")
}
}
....
// buildscript 블록에 따로 넣지 않아도 되는듯.
extra["minSdk"] = 23 //(?이거도 되는지 잘 모르겠다.)
rootProject.extra["minSdk"] = 23
// 가져올때,
extra["sdkVersion"] as Int //(?이거도 되는지 잘 모르겠다.)
rootProject.extra["sdkVersion"] as Int // buildscript같은 불록에 들어가 있으면, rootProject가 붙는듯.
// build.gradle.kts (app module level)
plugins {
...
}
// extra에서 변수로 받음. (dependency에서 공통으로 쓸 버전들)
val corektxVersion = rootProject.extra["core-ktxVersion"]
android {
compileSdk = rootProject.extra["compileSdk"] as Int
defaultConfig {
...
minSdk = rootProject.extra["minSdk"] as Int
targetSdk = rootProject.extra["targetSdk"] as Int
versionCode = rootProject.extra["versionCode"] as Int
versionName = rootProject.extra["versionName"] as String
}
...
}
...
dependencies {
implementation("androidx.core:core-ktx:${rootProject.extra["core-ktxVersion"]}")
...
}
// 내가 생각하는
ext(extra)를 사용의 장점과 단점
[장점]
- 프로젝트 분석시 가장 간단하면서 직관적이고 익숙한 방법일듯
(buildSrc나 toml파일생성 같은 별다른 작업없이, 그냥 gradle파일에 바로 작성가능하므로)
[단점]
- kts파일에서는 컨트롤 클릭하여, 참조하고 있는 값으로 이동이 안된다. (groovy에서는 되었던걸로 아는데)
- 그리고 ide 자동완성이 안됨. (+오탈자로 빌드에러 가능성)
-> 이 두가지 단점을 buildSrc를 이용해서 해결가능.
2. buildSrc 이용하기
// 이 블로그가 사용방법에 대해서 잘 작성되어있다.
https://leveloper.tistory.com/206
// gradle문서인데 , 이 문서가 여기랑 맞는건지 모르겠다.
https://docs.gradle.org/current/userguide/organizing_gradle_projects.html#sec:build_sources
내가 구현한 snippet과 노하우에대해서 작성해놓겠다.
내가 구현한것은 하나의 예제이므로 똑같이 만들필요는 없고,
다른 문서 찾아보고 알아서 바꿔야함.
gradle 특정 버전이 되어야 저거를 자동으로 잡지 않을까싶은데,?
몇 버전이 되어야하는지는 모르겠다.
(이글을 더 퀄리티 있게 쓰려면 나도 더 찾아봐야겠다..)
일단 buildSrc라는 폴더와 특정 파일을 만들고 gradle sync를 하면,
이 buildSrc이 뭔가 모듈처럼? 변하는것 같다. (settings.gradle에 include되지는 않지만.)
그래서 buildSrc에 작성한 코틀린 파일을,
gradle kts파일에서에서 import하여 사용하는 방식이다.
---------------------------------------------------
buildSrc 이용하기
1) buildSrc폴더 생성
안드로이드 스튜디오에서
프로젝트창 -> 프로젝트탭으로 변경
이 프로젝트의 루트 디렉토리에 buildSrc폴더 생성.
(project 이름 누르고 우클릭 new -> directory, app모듈과 같은 위치)
---------------------------------------------------
2) buildSrc 폴더에 build.gradle.kts파일 생성
buildSrc 폴더 우클릭 -> new -> file
build.gradle.kts를 입력하여 생성.
(혹시나 디렉토리로 만들어버리면 '.' 때문에 이상한 파일이 만들어질것이고 file로 만들어야함)
---------------------------------------------------
3) build.gradle.kts에 아래 내용 작성.
(나는 pokedex프로젝트를 따라 만들고 있어서, 위에 링크한 블로그와 내용이 좀 다르다.)
(뭐가 어떤건지는 따로 공서문서가 있는지 찾아봐야할듯하다?)
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
이 파일 내용 작성하고, gradle sync를 하면,
buildSrc폴더에
.gardle과 build폴더가 추가로 생기고
buildSrc폴더 아이콘 모양도 바뀌는것으로 기억한다.
---------------------------------------------------
4) 소스셋 디렉토리 생성
- buildSrc 폴더 우클릭 -> new -> directory
- 여기서 보면, gradle source sets들이 자동완성될 수 있게 정해져있는데
src\main\kotlin폴더를 생성했다.
---------------------------------------------------
5) 패키지 생성 & Configuration.kt파일 생성
-> 여기서 안드로이드 스튜디오 프로젝트 창에서 안드로이드 탭으로 변경하면
아마 buildSrc-main-kotlin으로 디렉토리 구조가 보일 것이고
- kotlin선택 -> 패키지생성 (com.example.yourproject, 패키지이름은 알아서..)
- 생성한 패키지 우클릭 -> new -> kotlin file
- 파일명 Configuration / object로 생성하였음.
---------------------------------------------------
6) Configuration에 내용작성
package com.example.pokedex_meltdown
object Configuration {
const val compileSdk = 32
const val targetSdk = 32
const val minSdk = 23
const val majorVersion = 0
const val minorVersion = 0
const val patchVersion = 1
const val versionName = "$majorVersion.$minorVersion.$patchVersion"
const val versionCode = 1
}
object Version {
const val CORE_KTX = "1.7.0"
}
이 파일내에 depenency에 version 넣는것도 테스트한다고,
Version이라고 object도 따로 넣었는데, 파일을 따로 2개만드는게 나을지도.?
---------------------------------------------------
7) build.gradle.kts (app level)에서,
buildSrc에서 작성한거 가져오기
// build.gradle.kts (:app)
import com.example.pokedex_meltdown.Version
import com.example.pokedex_meltdown.Configuration
...
plugins {
}
android {
compileSdk = Configuration.compileSdk
defaultConfig {
applicationId = "com.example.pokedex_meltdown"
//minSdk = 21
//minSdk = rootProject.extra["sdkVersion"] as Int
minSdk = Configuration.minSdk
targetSdk = Configuration.targetSdk
versionCode = Configuration.versionCode
versionName = Configuration.versionName
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
...
}
dependencies {
//implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.core:core-ktx:${Version.CORE_KTX}")
}
한 파일내에 obejct를 두개 넣어둬서 import를 저렇게 두개 넣어야했다.
이렇게 사용하면 되고, 그리 어렵지는 않지만 귀찮을 뿐이다.
---------------------------------------------------
8) .gitignore 파일 추가하기
여기서 추가적으로 git (VCS version control system)을 쓰고 있다면,
.gitignore파일을 생성해야하는것으로 보이는데,
buildSrc에서 그래들 빌드되면서,
.gradle 과 build폴더는 git에 추가가 되지 않아야 할 것으로 보였다.
그래서 buildSrc폴더 내에
.gitignore파일생성후, 아래 내용추가했음.
.gradle
/build
---------------------------------------------------
9)
그런데, 이 작업을 하고,
git사용중에 buildSrc를 삭제했거나
buildSrc가 없어야할 git branch이동을하면
없어야할 buildSrc폴더가 살아나고 하던데(?)
.gitignore에 등록해둔 파일들은 git브랜치 변경해도 그대로 남아있게되나?
.gitignore파일은 git에 따라 없어지면서 .gradle이랑 build폴더가 보이게 되는건가?
git에 대해서 좀 더 공부가 필요해보인다. ㅠㅠ
F5와 Reload From Disk를 해보고,
직접 디렉토리가보고 필요없으면 지워야할듯하다.
buildSrc삭제 했음에도, 그대로 또 android탭에서 보여지기도 하는데.
invalidate caches에서 VCS log..만 체크해서 돌려주면 됨.
(이런 현상이 생기는것과 모듈 생성후, 삭제할때도 버그같은게 생기는것이 뭔가 비슷한 느낌이다.)
https://hhyeok1026.tistory.com/25?category=964356
---------------------------------------------------
[내가 느낀 buildSrc방식의 장단점]
[장점]
- 컨트롤 클릭하여 참조되는 값을 보기 편함.
- gradle과 파일 분리가능.
[단점]
- gradle kts파일로 마이그레이션을 해야할 수도 있음.
- 다소 귀찮은 buildSrc폴더 만들기.
- 구글 공식문서에서 이 방식을 추천하지 않는다는 문구를 봤는데, 왜 그런지는 정확히 이해가 안감.(https://developer.android.com/studio/build/agp-upgrade-assistant?hl=ko)
3. libs.versions.toml 파일 이용하기(version catalog)
// 참고할만한 블로그
https://yjyoon-dev.github.io/android/2022/07/01/android-07/
// gradle 문서
https://docs.gradle.org/current/userguide/platforms.html
---------------------------------------------------
gradle의 version catalog 기능인,
gradle/libs.versions.toml 파일을 생성하여 버전관리하는 것을
찍먹해보았다.
대충 느낀점으로 ext, buildSrc에서는 공통사용 변수 & 버전들을 빼내어 썼다면,
이 기능은 dependency들의 '패키지명'을 포함하여 '버전'들을 싹다 빼버리는게 특징인것 같다.
version catalog랑 buildSrc랑 ext를 다 섞어서 쓸 수도 있는거 같은데
다 해보고 필요한거만 취해서 쓰면 되겠다.
------------------------------------------------------
libs.versions.toml 파일 이용하기
1) Gradle버전 검토.
version catalog가 7.0이상에서도 되는데, 7.4이하면 또 다른 코드를 넣어줘야한다고 해서,
그냥 toml파일만 쓰려면 Gradle 7.4버전 이상부터 된다고 카더라를 보았다.
pokedex project가 gradle 7.5.1을 사용하고 있길래,
나도 7.5.1로 변경하였다.
gradle버전 변경은
안드로이드스튜디오 - 프로젝트창 - 안드로이드탭 - gradle scripts - gradle-wrapper.properties에서
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
저기 보이는 gradle 버전 숫자를 변경하고 sync를 돌리면, 자동으로 다른 버전의 gradle을 다운받게된다.
(+기본적으로 생성되는 gradle디렉토리 구조에 대해서, 대강 공부를 하면 좋다.)
// gradle 기본사용법
https://velog.io/@franc/Gradle-%EA%B8%B0%EB%B3%B8%EC%82%AC%EC%9A%A9%EB%B2%95
-----------------------------------------------------------------------------------------
2) projectroot/gradle/libs.versions.toml 파일 생성하기
[안드로이드 스튜디오] - [프로젝트 창] - [프로젝트 탭]
프로젝트 펼치고 -> gradle폴더 찾아서
gradle폴더내에 libs.versions.toml 파일을 생성.
필요한 내용은 알아서 [version], [plugins], [libraries] 패키지명이랑 version에 적어둔 이름을 적어주면 된다.
[versions]
agp = "7.2.2"
kotlin = "1.7.10"
material = "1.6.1"
androidxCore = "1.7.0"
androidxAppcompat = "1.5.1"
junit = "4.13.2"
androidxTestJunit = "1.1.3"
espresso = "3.4.0"
hilt = "2.42"
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
[libraries]
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidxAppcompat" }
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidxCore" }
material = { module = "com.google.android.material:material", version.ref = "material" }
agp = { module = "com.android.tools.build:gradle", version.ref = "agp" }
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
hilt-plugin = { module = "com.google.dagger:hilt-android-gradle-plugin", version.ref = "hilt" }
# androidTest
androidx-junit = { module = "androidx.test.ext:junit", version.ref = "androidxTestJunit" }
androidx-espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" }
# unit test
junit = { module = "junit:junit", version.ref = "junit" }
위의 소스는 내가 작성해본것이고,
자세한 설명은 다른 문서를 찾아보시길.. (아직 공부가 덜 되서 설명할수가 없다..)
[version], [plugins], [libraries] 말고도,
[bundle]도 있었고..
아직 group키워드는 뭔지 모르겠다.
-----------------------------------------------------------------------------------------
3) 사용하기
- build.gradle.kts (project level)
// pokedex에 있는 방식 (build.gradle.kts (project level))
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
alias(libs.plugins.spotless)
}
buildscript {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath(libs.agp)
classpath(libs.kotlin.gradlePlugin)
classpath(libs.hilt.plugin)
}
}
현재 setting.gradle.kts에서
centralized repository declaration으로 repository위치 선언이 되어있는데,
project level에서,
[buildscript]-[repositories]가 필요한가? 의문이고,
[buildscript]-[depencies]에서 가져오는 저 라이브러리들은 뭔지 공부를 해야겠다.
build.gradle 파일 내용들에 대해서 빠삭하게 알지못하면, version catalog은 제대로 사용을 못하겠다...
// 나는 기본 프로젝트 생성시, 디폴드로 생성되는 플러그인만 집어 넣어봤고,
// 일단 내가 작성한 방법 (build.gradle.kts (project level))
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
/*id("com.android.application") version "7.2.1" apply false
id("com.android.library") version "7.2.1" apply false
id("org.jetbrains.kotlin.android") version "1.6.21" apply false*/
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.android) apply false
}
...
buildscript {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
dependencies {
//classpath(libs.plugins.android.application)
//classpath(libs.plugins.android.library)
//classpath(libs.plugins.kotlin.android)
//classpath(libs.agp)
//classpath(libs.kotlin.gradlePlugin)
//classpath(libs.hilt.plugin)
}
}
...
libs.versions.toml을 사용하면, 문법을 찾아보면
plugins블록에서 alias(libs.plugins.pluginname)으로 가져오는게 맞는데,
libs부분에 빨간색으로 경고가 나온다.
'val Project.libs: LibrariesForLibs' can't be called in this context by implicit receiver. Use the explicit one if necessary
-> @Suppress("DSL_SCOPE_VIOLATION")으로 어노테이션을 달아주면 된다.
이 경고 나와도 문법제대로 작성하면 싱크는 제대로 되는듯하고, 어노테이션은 있으나 없으나 인듯.?
plugin설정에서 id()로 을 사용할때는 뒤에 apply false를 붙이는 것은 projectlevel에서는 사용하지 않고,
미리 이름만 쓸 수 있도록 정해둔다고 봤었다.
그래서 모듈에서는 버전 안쓰고 패키지이름만 작성해서 쓴다고 gradle문서에서 본거 같은데,
(아래링크 참고)
https://docs.gradle.org/current/userguide/plugins.html#sec:subprojects_plugins_dsl
alias키워드로 plugin을 가져오면 어떻게 되는건지 몰라서
일단, 뒤에 apply false를 붙여놨다.(?)
혹시나, [buildscript]-[depencies]블록에 classpath()로 라이브러리 말고,
플러그인도 작성할 수 있는지 돌려봤는데,
warning이 나와서 아닌것 같아서 alias에 작성하였다.
...
이 부분에대해서 문법을 더 공부해야한다.
----
- build.gradle.kts (app level)
// pokedex에 있는 방식 (build.gradle.kts (app level))
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
id(libs.plugins.android.application.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
id(libs.plugins.kotlin.kapt.get().pluginId)
id(libs.plugins.kotlin.parcelize.get().pluginId)
id(libs.plugins.hilt.plugin.get().pluginId)
}
...
dependencies {
...
// androidx
implementation(libs.material)
implementation(libs.androidx.fragment)
implementation(libs.androidx.lifecycle)
implementation(libs.androidx.startup)
...
}
id()를 이용해서 뒤에 get()을 이용해서 id를 가져오는데,
이게 또 맞는지 의문이다. -> 일단 잘되는것 같았다.
------
// 내가 작성한 방식 (build.gradle.kts (app level))
기본생성되는 plugin과 dependency에 대해서만 수정했다.
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
/*id("com.android.application")
id("org.jetbrains.kotlin.android")*/
/*
id(libs.plugins.android.application.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
*/
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
...
dependencies {
/*
implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.appcompat:appcompat:1.5.1")
implementation("com.google.android.material:material:1.6.1")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.3")
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
*/
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.material)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso)
}
dependencies부분은 같고,
plugin부분만 alias로 가져왔다.
문법은 차차 공부해보면서
뭐가 좋은건지 찾아봐야겠다.
--------
여기서 참조하는 변수들을,
buildSrc는 컨트롤 좌클릭하면 무슨 버전인지 참조되는 소스코드로 바로 이동되었지만,
version catalog는 컨트롤 좌클릭시 .gradle/../LibrariesForLibs.java파일로 이동된다.
이 LibrariesForLibs.java 파일은 그래들 싱크 돌리면,
auto generate되는 파일인것 같고?
이 파일에서 주석을 보면, libs.versions.toml의 존재와, 여기에 작성해둔 별칭들에 대해 알 수 있다.
이 파일을 컨트롤 좌클릭으로 들어와서 -> libs.versions.toml을 복사 -> 쉬프트 두번 -> 붙여넣기해서 검색해서
저 libs.versions.toml을 띄우면 될 듯하다. (project로 열어서 gradle폴더 열기 귀찮으니까)
--------------------------------
내가 생각하는 version catalog
libs.versions.toml 파일 사용에 대한 장점과 단점
[장점]
- 아래 링크한 헤이딜러 블로그를 보면, groovy파일이라서 kts파일로 마이그레이션하기는 좀 그렇고,
buildSrc를 사용하지 못하는경우에만 사용을 시도해볼만 하겠다.
// 헤이딜러 블로그
--------
[단점]
- version catalog는 gradle.build파일에서 사용해보려니 뭔가 직관적으로 안되고 계속 막혔었다.
어노테이션 뭐 달아야하는 버그도 있고, 한글문서보다는 영어문서를 쭉 찾아다녀야할듯한데,
선구자가 될분들아니면 개인적으로 현재 2022.10.05 기준으로는 추천해주고싶지는않다.
(내가 잘 몰라서 그렇겠지만..)
- 나같은 초보입장에서는 gradle/libs.versions.toml 파일에 적어둔 내용만 가독성이 좋아보이고(?)
실제로 프로젝트 분석하려면, 이 방식이 뭔지 모르면 dependency버전이 어떻게 빌드되는지도 모른다.
특히, gradle/libs.versions.toml파일이 [안드로이드 스튜디오] - [프로젝트 창] - [프로젝트 탭]으로 띄워야 gradle폴더를 볼 수 있는데, 여기를 안열어봤으면, dependency 어떻게 주는지 검색도 못 했다.
4. 업데이트할 dependency가 있는지 확인하는법
(아직 안해봐서 해보고 더 작성할 예정)
-> 목차에 있는 링크보고 직접 해보시길..
나는 나중에 해야겠다
[결론]
이 글을 독자가 다 보았을리가 없을듯하다...
가독성도 별로라서 띄워쓰기도 더 넣어보긴 했는데, 너무 긴거 같다.
폰트서체도 마음에 안든다. 글씨도 작다.
글을 더 보기 좋게 잘 쓰기위한 연구가 필요하다.
공부를 좀 더 해서 퇴고를 해둬야겠다.
//build.gradle에서 buildscript랑 api들 플러그인들에 대해서 좀 더 공부해야한다.
version catalog도 제대로 쓰려면 문법들 더 정리해야함.
'Android > Gradle' 카테고리의 다른 글
[Android Gradle] Groovy에서 kts파일로 마이그레이션 하기 (0) | 2022.10.02 |
---|---|
Android Studio에서 기본 생성되는 gradle파일에 대한 분석 (야매 주의!) (0) | 2022.10.01 |