Android Build.Gradle ์ค๋ช ๐
13593 ๋จ์ด androidgroovybuildgradlejava
Gradle์ Android ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐ ๋จ๊ณ๋ฅผ ์๋ํํ๋ ๋น๋ ์์คํ ์ ๋๋ค.
Gradle ์์คํ ์์ Java ํ๋ซํผ์ฉ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋์ ์ฐ๋ฆฌ๋ ๋งค์ฐ ๊ฐ๋จํ ์ธ์ด์ธ Groovy ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ฅผ ์ฌ์ฉํฉ๋๋ค. Groovy๋ Java๋ณด๋ค ํจ์ฌ ๊ฐ๋จํ ์ธ์ด์ ๋๋ค. ์ด๋ฌํ ์ฐจ์ด์ ์ ์ค๋ช ํ github repo๋ฅผ ์ดํด๋ณผ ์ ์์ต๋๋ค.
์ด ๊ทธ๋ฃจ๋น๋ ๋ฌด์์ ๋๊น?
Groovy๋ ๊ฐ๊ฒฐํ๊ณ ๋จ์ํ๋ฉฐ ๋ฐฐ์ฐ๊ธฐ ์ฌ์ด ๊ตฌ๋ฌธ์ ํตํด ๊ฐ๋ฐ์ ์์ฐ์ฑ์ ๋์ด๋ ๊ฒ์ ๋ชฉํ๋ก ํ๋ Java ํ๋ซํผ์ฉ ์ ์ ํ์ดํ ๋ฐ ์ ์ ์ปดํ์ผ ๊ธฐ๋ฅ์ ๊ฐ์ถ ๊ฐ๋ ฅํ ์ฃผ๋ฌธํ ์ ํ ๋์ ์ธ์ด์ ๋๋ค.
Groovy์์ ๋ฐฐ์ด ์ด ์ ๋ณด๋ฅผ ์ฌ์ฉํ์ฌ ์ ๋ฌธ์ ์ธ ๋ฐฉ์์ผ๋ก Android Studio์์ build.gradle์ ๋น๋ํ๋ ๋ฐฉ๋ฒ์ ์์ธํ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
Build.gradle์ ์ฌ์ฉํ๋ฉด Android Studio๋ฅผ ํตํด ๊ฐ๋ฐํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐ ํ๋ก์ธ์ค๋ฅผ ๋ณ๊ฒฝํ๊ณ ์ฌ๋ฌ ํ๊ฒฝ(๋๋ฒ๊ทธ , ent, test, beta, prod.. ) ํ๊ณผ ํธ์ํ๊ฒ ์์ ํ ์ ์์ต๋๋ค.
Gradle(์ฑ) ๋๊ตฌ ๋น๋
์ผ๋ฐ์ ์ธ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
`ํ๋ฌ๊ทธ์ธ {
id 'com.android.application'
}
java.util.regex.Pattern ๊ฐ์ ธ์ค๊ธฐ
์๋๋ก์ด๋ {
์ปดํ์ผ SDK 32
defaultConfig {
applicationId "com.hakki.uygulamaadi"
์ต์ SDK 21
targetSdk 32
resConfigs "en", "tr"
...
}
}
๋น๋ ์ ํ {}
์ปดํ์ผ ์ต์ {}
์ข ์์ฑ {
๊ตฌํ 'androidx.appcompat:appcompat:1.4.1'
...
}`
์ด์ build.gradle ์ค์ ์ ๋ฌด์์ด ์๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
signingConfigs: ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒ์ํ ๋ ์ํ๋ ํค๊ฐ ์ ์ฅ๋ฉ๋๋ค. ๋ฆด๋ฆฌ์ค๊ฐ ๋ฒ์ ์ ์ถ๊ฐ๋ฉ๋๋ค.
buildTypes: ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ ํ๋ก์ธ์ค๋ฅผ ์ถ๊ฐํ๊ณ ์์ ํ๋ ๋ถ๋ถ์ ๋๋ค.
buildConfigField: ๋น๋๋ฅผ ๊ฐ์ ธ์ค๋ ๋์ ์์ ๊ฐ์ ๋ณด๋ผ ์ ์์ต๋๋ค.
Build๊ฐ ์์ ๋๋ฉด BuildConfig.java ํด๋์ค๊ฐ ์์ฑ๋ฉ๋๋ค. ํด๋์ค์์ ์ด ์์์ ์ก์ธ์คํ๋ ค๋ฉด BuildConfig.INFO๋ก ์ก์ธ์คํด์ผ ํฉ๋๋ค. ์ฐธ๊ณ : ์์ง ๋น๋๋ฅผ ๋ฐ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ์ค๋ฅ๊ฐ ์๋ ๊ฒ์ฒ๋ผ ๋นจ๊ฐ์์ผ๋ก ํ์๋ฉ๋๋ค.
productFlavors: ํ ์ง๋ถ ์๋ ์ฌ๋ฌ ํ๋ก์ ํธ๋ฅผ ์์งํ๋ ค๋ ๊ฒฝ์ฐ์ ์ฌ์ฉ๋ฉ๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก "main"์ผ๋ก ์ ๊ณต๋๋ฉฐ build.gradle์ ํฌํจ๋์ง ์์ต๋๋ค. FlavorDimensions๋ ๋ฏธ๋ฆฌ ์ ์๋ฉ๋๋ค.
manifestPlaceholders: ์ ํ๋ฆฌ์ผ์ด์ ์์ด์ฝ์ ๋ณ๊ฒฝํฉ๋๋ค.
resValue: ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ๋ผ ๋ฆฌ์์ค ๊ฐ์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
์: ์์ฉ ํ๋ก๊ทธ๋จ ์ด๋ฆ์ด ์ฌ๋ฌ ๊ณณ์์ ์ธ๊ธ๋๋ฉด(์ผ๋ฐ์ ์ธ ์คํ๋์ ๋๋ ํ๋์ผ ์ ์์) ์ฌ๊ธฐ์ ๊ฐ์ ํ ๋นํ์ฌ ์ญ๋์ฑ์ ์ถ๊ฐํ ์ ์์ต๋๋ค.
variantFilter: ๋ ๋ง์ buildType์ ์ถ๊ฐํ ์๋ก ๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ ๋ง์ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ถ๊ฐํ ์ ์์ต๋๋ค. ์. ํ ์คํธ, ๋๋ฒ๊ทธ, ๋ฒ ํ, ์ํธ, ๋ฆด๋ฆฌ์ค์ ๊ฐ์ ํ๋ก์ธ์ค๋ฅผ ํ ์ผ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ถ๊ฐํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ชจ๋ ์ ํ๋ฆฌ์ผ์ด์ ์๋ ํ๋ก์ธ์ค๊ฐ ๊ทธ๋ฐ ์์ผ๋ก ์๋ํ์ง ์๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค. ๋ถํ์ํ๊ฒ ๊ฐ๋ฐ ํ๊ฒฝ ๋ชฉ๋ก์ ์ฑ์ฐ์ง ์๋๋ก variantFilter๋ฅผ ์ฌ์ฉํ์ฌ ์ ๊ฑฐํ๊ณ ์์ต๋๋ค.
๋ค๋ฅธ ์ค์ํ ์ฌํญ์ ๊ฐ๋จํ ์ค๋ช ํด์ผ ํ๋ ๊ฒฝ์ฐ:
ํ๋ก์ ํธ์ ๋ชจ๋ ์์ฉ ํ๋ก๊ทธ๋จ์ ๋ชจ์ผ๋ ๊ฒ์ ๋ณ๊ฒฝ ๋ฐ ๊ด๋ฆฌ ์ธก๋ฉด์์ ํธ๋ฆฌํจ์ ์ ๊ณตํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ถ๊ฐํด์ผ ํ ๋ช ๊ฐ์ง ์ฌ์ํ ๊ฐ์ ์ฌํญ๋ ์ ๊ณตํฉ๋๋ค.
์ด๊ฒ๋ค;
์ด๋ฌํ ๋ฌธ์ ์ ๋ํด ๋ด๊ฐ ๊ฐ๋ฐํ ์๋ฃจ์ ๊ณผ ํจ๊ป ์๋์ ์ํ build.gradle ์ฝ๋๋ฅผ ์ดํด๋ณผ ์ ์์ต๋๋ค.
๊ทธ๋ค(์ฑ)์ ๋ชจ๋ ์ง๋ฐฐํ๋ ํ๋์ ์๋๋ก์ด๋ ํ๋ก์ ํธ.
plugins {
id 'com.android.application'
}
import java.util.regex.Matcher
import java.util.regex.Pattern
/*
sonarqube {
properties {
property "sonar.projectName", "LargeProjectExample"
property "sonar.projectKey", "LargeProjectExample"
property "sonar.host.url", "http://localhost:9000"
property "sonar.language", "java"
property "sonar.login", "admin"
property "sonar.password", "******"
}
}
*/
android {
useLibrary 'org.apache.http.legacy'
compileSdk 32
defaultConfig {
applicationId "com.largeproject.example"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
resConfigs "en", "tr"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
signingConfigs {
release {
keyAlias 'key0'
keyPassword '******'
storeFile file('27tkey.jks')
storePassword '******'
}
}
buildTypes {
// Development Environments
release {
signingConfig signingConfigs.release
// zipAlignEnabled true //
//shrinkResources true //res
minifyEnabled true //java code
// multiDexEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
debuggable false
}
debug {
//applicationIdSuffix ".debug"
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
debuggable true
}
dev {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
debuggable true
/* DEV ORTAMI */
buildConfigField "String", "INFO", '"someinfodev"'
}
plat {
/* PLATFORM ORTAMI */
buildConfigField "String", "INFO", '"someinfoplat"'
}
prod {
/* PROD ORTAMI */
buildConfigField "String", "INFO", '"someinfoprod"'
}
}
flavorDimensions "APP"
productFlavors {
largeProject {
dimension "APP"
// CHANGE
String appName = "Large Project"
String appVersionName = "4.1.55"
Integer appVersionCode = 255
String appIdSuffix = ".largeproject"
// CHANGE
manifestPlaceholders = [appName: appName, appIcon: "@drawable/ic_second"]
resValue("string", "app_name", appName)
versionName appVersionName
versionCode appVersionCode
applicationIdSuffix appIdSuffix
}
largeProject2 {
dimension "APP"
String appName = "Another Large Project"
String appVersionName = "4.1.55"
Integer appVersionCode = 255
String appIdSuffix = ".largeProject2"
manifestPlaceholders = [appName: appName, appIcon: "@drawable/ic_second"]
resValue("string", "app_name", appName)
versionName appVersionName
versionCode appVersionCode
applicationIdSuffix appIdSuffix
buildConfigField 'String', 'merhabaa', '"selam"'
}
}
// Ignore Operations. Note: "==" operator is better than ".contains".
variantFilter { variant ->
String app = variant.flavors*.name.get(0).toLowerCase()
String buildType = variant.buildType.name.toLowerCase()
// println("app : " + app + " buildType : " + buildType)
if (app == "largeproject" && buildType == "ent") {
variant.setIgnore(true)
}
}
// Changing the Default Media Storage Location Groovy
List<String> resBuildTypes = new ArrayList<String>()
List<String> resBuildType = new ArrayList<String>()
resBuildTypes.add("src/main/res/common")
productFlavors.all { flavor ->
String folder = "src/main/res/customers/" + flavor.name
resBuildTypes.add(folder)
resBuildType.add(flavor.name)
}
//println resBuildTypes // output: [src/main/res/common, src/main/res/customers/largeProject, src/main/res/customers/largeProject2]
//println resBuildType //output: [largeProject, largeProject2]
sourceSets {
main.res.srcDirs = resBuildTypes
// also you can change manifest file for every customer
//main.manifest.srcFile = "src/main/res/"
for (String var : resBuildType) {
"$var" {
setRoot "src/main/res/" + var
}
}
}
buildFeatures {
buildFeatures.dataBinding = true
buildFeatures.viewBinding = true
}
}
// Build task that allows us to get the received project request information.
def projectName
def projectBuildType
task getCurrentFlavor() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern
// take buildTypes to String
String buildTypes = ""
android.buildTypes.all {
type ->
//println(type.name) Capitalize
String output = type.name.substring(0, 1).toUpperCase() + type.name.substring(1)
buildTypes += output + "|"
}
buildTypes = buildTypes.substring(0, buildTypes.length() - 1) //delete last | character
//println buildTypes
if (tskReqStr.contains("assemble"))
pattern = Pattern.compile("assemble(\\w+)($buildTypes)") // Addition Required for Every BuildType Created. Dev|Ent|Tst,
else
pattern = Pattern.compile("generate(\\w+)($buildTypes)") // $buildTypes fulfills this request by automating.
Matcher matcher = pattern.matcher(tskReqStr)
if (matcher.find()) {
//projectVariant = matcher.group().toLowerCase()
projectName = matcher.group(1).toLowerCase() // Changing to 2 will return build type, 1 provides product flavor
projectBuildType = matcher.group(2).toLowerCase() // Changing to 2 will return build type, 1 provides product flavor
} else {
println "NO MATCH FOUND"
}
}
task buildVariantTasks(type: Copy) {
dependsOn getCurrentFlavor
println "flavor name is " + projectName
println "build type is " + projectBuildType
if(projectName!=null && projectBuildType!=null) {
// Separating Classes and Resourses
// To identify location of Common (Main) Classes, simply change the part that says "common".
String appID = android.defaultConfig.applicationId
String appIdSlashes = "src/main/java/" + appID.replaceAll("\\.", "/") + "/"
android.sourceSets.main {
java.srcDirs = [appIdSlashes + "common", appIdSlashes + projectName]
res.srcDirs = ['src/main/res/common', 'src/main/res/'+ projectName]
manifest.srcFile "src/main/res/customers/" + projectName + "/AndroidManifest.xml"
}
android.applicationVariants.all { variant ->
// Output APK Name & appVer - Specify a Common BuildConfig to Add to Each Product
String buildType = variant.buildType.name
String flavorName = variant.getFlavorName()
String buildTime = new Date().format("yyMMddHHmm", TimeZone.getTimeZone("Asia/Istanbul"))
variant.outputs.all {
buildConfigField 'String', 'INFO', "\"${flavorName}.${buildType}\""
buildConfigField 'String', 'appVer', "\"${versionName}_${buildTime}\""
outputFileName = getFileName(flavorName, versionName, buildTime, buildType)
}
// Action By Product Specific Defined BuildConfigField, If Product Is Defined ...
/*variant.productFlavors.each { flavor ->
if (variant.getFlavorName() == projectName) {
flavor.buildConfigFields.each { key, value ->
if(key == "INFO") {
println value.type
println value.name
println value.value
}
}
}
}*/
// Release Actions for Release -> Allatori is here.
if(projectBuildType.contains("release") && flavorName == projectName) {
// You can add here compression tools...
}
}
}
}
preBuild.dependsOn buildVariantTasks
static String getFileName(flavorName, versionName, buildTime, buildType) {
return flavorName + "_" + versionName + "_" + buildTime + "_" + buildType + ".apk"
}
// Example of defining version for dependencies
ext {
appCombat = '1.4.1'
material = '1.5.0'
constrain = '2.1.3'
junit = '4.13.2'
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:' + appCombat
implementation 'com.google.android.material:material:'+ material
implementation 'androidx.constraintlayout:constraintlayout:'+constrain
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:'+junit
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
๋ค์ ๊ธฐ์ฌ์์๋ Kotlin์ ์ฌ์ฉํ์ฌ ๊ฐ๋ฐ ํ๋ก์ธ์ค์์ ์ด๋ค ๊ฐ์ ์ฌํญ์ ๋ง๋ค ์ ์๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์์ฒ:
Gradle
What it this Groovy?
Github Build.Gradle
Reference
์ด ๋ฌธ์ ์ ๊ดํ์ฌ(Android Build.Gradle ์ค๋ช ๐), ์ฐ๋ฆฌ๋ ์ด๊ณณ์์ ๋ ๋ง์ ์๋ฃ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋งํฌ๋ฅผ ํด๋ฆญํ์ฌ ๋ณด์๋ค https://dev.to/hkkcngz/android-buildgradle-explained-c8nํ ์คํธ๋ฅผ ์์ ๋กญ๊ฒ ๊ณต์ ํ๊ฑฐ๋ ๋ณต์ฌํ ์ ์์ต๋๋ค.ํ์ง๋ง ์ด ๋ฌธ์์ URL์ ์ฐธ์กฐ URL๋ก ๋จ๊ฒจ ๋์ญ์์ค.
์ฐ์ํ ๊ฐ๋ฐ์ ์ฝํ ์ธ ๋ฐ๊ฒฌ์ ์ ๋ (Collection and Share based on the CC Protocol.)
์ข์ ์นํ์ด์ง ์ฆ๊ฒจ์ฐพ๊ธฐ
๊ฐ๋ฐ์ ์ฐ์ ์ฌ์ดํธ ์์ง
๊ฐ๋ฐ์๊ฐ ์์์ผ ํ ํ์ ์ฌ์ดํธ 100์ ์ถ์ฒ ์ฐ๋ฆฌ๋ ๋น์ ์ ์ํด 100๊ฐ์ ์์ฃผ ์ฌ์ฉํ๋ ๊ฐ๋ฐ์ ํ์ต ์ฌ์ดํธ๋ฅผ ์ ๋ฆฌํ์ต๋๋ค