Gradle Build Lifecycle: Tasks, Phases & Incremental Builds - සිංහලෙන් SC Guide

Gradle Build Lifecycle Unveiled: A Sri Lankan Dev's SC Guide
කොහොමද යාලුවනේ! අද අපි කතා කරන්න යන්නේ ඔයාලා ගොඩක් දෙනෙක් දිනපතාම වගේ පාවිච්චි කරන, ඒත් සමහර විට එච්චරම ගැඹුරින් තේරුම් අරගෙන නැති දෙයක් ගැන. ඒ තමයි අපේ Gradle Build Lifecycle එක!
අපි software එකක් හදනකොට, code compile කරනවා, tests run කරනවා, packages හදනවා, deploy කරනවා... මේ හැමදේම manual කරන්න ගියොත් වැඩේ එපා වෙනවා නේද? එතකොට තමයි Gradle වගේ build automation tool එකක් අපිට පිහිටට එන්නේ. Gradle කියන්නේ modern applications හදනකොට අත්යවශ්යම tool එකක් කියලා ඔයාලා දන්නවා ඇති.
ඒත්, Gradle build එකක් run කරනකොට ඇතුළතින් මොකද වෙන්නේ කියලා ඔයාලා කවදාවත් හිතල තියෙනවද? Task එකක් run වෙනකොට, ඒක හරියටම මොන අදියරවල් හරහාද යන්නේ? මේවා ගැන හරියටම දැනගත්තොත් ඔයාලට පුළුවන් build times අඩු කරගන්න, common issues resolve කරගන්න, සහ වඩාත් efficient විදිහට build scripts ලියන්න. අද අපි මේ Gradle build එකක ඇතුළත මොකද වෙන්නේ කියලා ගැඹුරින් බලමු. ඒ කියන්නේ, Gradle Build Lifecycle එක ගැන!
Gradle කියන්නේ මොකක්ද?
සරලව කිව්වොත්, Gradle කියන්නේ ඉතාමත් powerful සහ flexible build automation tool එකක්. ඒක Apache Maven වගේම build tool එකක් වුණත්, Maven වලට වඩා flexibility එකක් දෙනවා. Gradle වලදී අපිට Groovy හෝ Kotlin DSL (Domain Specific Language) පාවිච්චි කරලා build scripts ලියන්න පුළුවන්. මේ නිසා build scripts programmatically ලියන්න පුළුවන්කම ලැබෙනවා. ඒකෙන් code එක වගේම readable සහ maintainable වෙනවා.
Gradle පාවිච්චි කරන්නේ Java, Kotlin, Android projects වලට විතරක් නෙමෙයි. C++, Swift, JavaScript වගේ ගොඩක් technologies වලට support කරනවා. Dependency Management, Task Orchestration, Incremental Builds වගේ features නිසා development process එක වේගවත් කරනවා වගේම, Continuous Integration (CI) සහ Continuous Delivery (CD) pipelines වලටත් හොඳටම ගැලපෙනවා.
Build Lifecycle එකේ Stages
Gradle build එකක් start වෙනකොට, ඒක ප්රධාන අදියර තුනක් (stages) හරහා යනවා. මේවා තමයි:
- Initialization Phase (ආරම්භක අදියර)
- Configuration Phase (සැකසුම් අදියර)
- Execution Phase (ක්රියාත්මක කිරීමේ අදියර)
දැන් අපි මේ එක් එක් අදියර ගැන විස්තරාත්මකව බලමු.
1. Initialization Phase (ආරම්භක අදියර)
මේක තමයි build එකේ මුලින්ම සිදුවෙන දේ. මේ අදියරේදී Gradle, build කරන්න ඕනේ මොන projects ද කියලා තීරණය කරනවා. ඒ වගේම, ඒ projects වලට අදාළ settings මොනවද කියලාත් load කරගන්නවා.
Multi-project builds වලදී මේක ගොඩක් වැදගත්. උදාහරණයක් විදිහට, ඔයාලගේ project එකට separate modules (e.g., `app`, `library`, `core`) තියෙනවා නම්, ඒ හැම module එකක්ම වෙන වෙනම project එකක් විදිහට Gradle recognize කරගන්නේ මේ අදියරේදී තමයි.
මේ සඳහා Gradle, project එකේ root folder එකේ තියෙන settings.gradle
(හෝ settings.gradle.kts
) file එක පාවිච්චි කරනවා. මේ file එකේ තමයි sub-projects declare කරන්නේ:
// settings.gradle
rootProject.name = 'MyAwesomeProject'
include 'app', 'library', 'core'
මේ settings.gradle
file එකේදී, Gradle තීරණය කරනවා මේ build එකේදී ඇතුළත් කරන්න ඕනේ projects මොනවද, ඒ projects වලට අදාළ project directories මොනවද කියන දේවල්. මේ phase එක අවසානයේදී, Gradle ට build එකට අදාළ හැම Project instance එකක්ම තියෙනවා.
2. Configuration Phase (සැකසුම් අදියර)
මේක තමයි build lifecycle එකේ හදවත කියලා කියන්න පුළුවන්. Initialization phase එකෙන් identify කරගත්තු හැම project එකකටම අදාළව තියෙන build.gradle
(හෝ build.gradle.kts
) script එක Gradle මේ අදියරේදී run කරනවා.
මේ scripts වල තමයි tasks නිර්මාණය කරන්නේ, dependencies resolve කරන්නේ, plugins apply කරන්නේ සහ build එකට අදාළ අනෙකුත් configurations සඳහන් කරන්නේ. මේ සියලුම information එකතු කරගෙන, Gradle විසින් Task Graph එකක් හදනවා. Task Graph එක කියන්නේ, මොන task එකට පස්සේ මොන task එකද run වෙන්න ඕනේ කියලා තියෙන dependencies set එකක්.
උදාහරණයක් විදිහට, ඔයාලට code compile කරන්න කලින් clean කරන්න ඕනේ නම්, ඒ dependency එක task graph එකේ සටහන් වෙනවා. Task graph එක හැදුනට පස්සේ, Gradle දන්නවා මොන tasks run කරන්න ඕනෙද, මොන පිළිවෙළටද run කරන්න ඕනේ කියන දේවල්.
මේ අදියරේදී මතක තියාගන්න වැදගත් දෙයක් තමයි, build එකක් run කරන හැම වාරයකදීම මේ Configuration Phase එක සම්පූර්ණයෙන්ම run වෙනවා. ඒ නිසා, මේ scripts ඇතුළේ unnecessary logic හෝ heavy computations තියෙනවා නම්, ඒක build time එකට බලපාන්න පුළුවන්. ඒකෙන් performance issue එන්න පුළුවන්. Simple Print statement එකක් දැම්මොත් වුණත්, ඒක හැම වාරයකදීම console එකේ print වෙනවා.
// build.gradle (හෝ build.gradle.kts)
// මේක Configuration Phase එකේදී run වෙනවා
println 'Hello from Configuration Phase!'
task myTask {
// මේක Execution Phase එකේදී run වෙනවා
doLast {
println 'Hello from Execution Phase!'
}
}
ඔයාලා gradle myTask
කියලා run කරලා බලන්න, Hello from Configuration Phase!
කියන එක මුලින්ම print වෙලා, පස්සේ Hello from Execution Phase!
කියන එක print වෙනවා. මේකෙන් ඔයාලට හොඳටම තේරේවි Configuration Phase එක හැම build එකකදීම run වෙනවා කියලා.
3. Execution Phase (ක්රියාත්මක කිරීමේ අදියර)
Configuration phase එකෙන් සාර්ථකව Task Graph එක හැදුනට පස්සේ, මෙතනදී tasks ඇත්තටම run වෙනවා. මේ අදියරේදී Gradle විසින් task graph එකේ තියෙන පිළිවෙළට tasks එකින් එක ක්රියාත්මක කරනවා.
මේ අදියරේ තියෙන වැදගත්ම feature එකක් තමයි up-to-date check එක. Gradle විසින් task එකක් run කරන්න කලින් බලනවා, ඒ task එකේ inputs (e.g., source files, configurations) සහ outputs (e.g., compiled classes, JAR files) කලින් run කරපු වෙලාවේ ඉඳන් වෙනස් වෙලා තියෙනවද කියලා. කිසිම වෙනසක් වෙලා නැත්නම්, Gradle ඒ task එක 'UP-TO-DATE' කියලා mark කරලා skip කරනවා. මේක තමයි Incremental Builds වලට පදනම. මේ ගැන අපි තව ටිකකින් විස්තරාත්මකව කතා කරමු.
compileJava
, test
, jar
වගේ commonly used tasks මේ අදියරේදී තමයි run වෙන්නේ. Task එකක් run වෙනකොට, ඒකේ actions (doFirst
, doLast
, හෝ generic actions) execute වෙනවා.
Tasks එක්ක වැඩ කරමු
Gradle build එකකදී හැම වැඩක්ම කරන්නේ tasks වලින්. Task එකක් කියන්නේ build process එකේදී run කරන්න පුළුවන් single atomic piece of work එකක්. උදාහරණයක් විදිහට, file එකක් copy කරන එක, code compile කරන එක, tests run කරන එක වගේ දේවල් tasks විදිහට සලකන්න පුළුවන්.
අපි සරල task එකක් හදමු:
// build.gradle
task hello {
doLast {
println 'Hello from Gradle Task!'
}
}
මේ task එක run කරන්න, ඔයාලගේ terminal එකේ project root folder එක ඇතුළේ ඉඳන් gradle hello
කියලා type කරලා Enter කරන්න. එතකොට ඔයාලට Hello from Gradle Task!
කියලා output එක ලැබෙයි.
Tasks අතර dependencies add කරන්නත් පුළුවන්. උදාහරණයක් විදිහට, compile
task එක run කරන්න කලින් clean
task එක run වෙන්න ඕනේ කියලා අපිට කියන්න පුළුවන්:
// build.gradle
task clean {
doLast {
println 'Cleaning up build directory...'
delete 'build'
}
}
task compile(dependsOn: 'clean') {
doLast {
println 'Compiling source code...'
}
}
දැන් gradle compile
කියලා run කරාම, මුලින්ම clean
task එක run වෙලා, ඊට පස්සේ compile
task එක run වෙනවා. මේ වගේ dependencies වලින් තමයි Gradle Task Graph එක හදන්නේ.
Incremental Builds - වේගෙ වැඩිකරමු!
Gradle වල තියෙන සුපිරිම feature එකක් තමයි Incremental Builds. මේකෙන් වෙන්නේ, කලින් build කරපු දේවල් ආපහු build නොකර, වෙනස් වෙච්ච කොටස් විතරක් build කරන එක. මේකෙන් build time එක පුදුමාකාර විදිහට අඩු වෙනවා, විශේෂයෙන්ම විශාල projects වලදී. අප්පට සිරි! මේක කොච්චර වටිනවද නේද?
Gradle මේක කරන්නේ 'input/output caching' සහ 'up-to-date checks' වලින්. Task එකක් run වෙනකොට, Gradle ඒ task එකේ inputs (e.g., source files, configuration parameters) සහ outputs (e.g., compiled class files, JAR files) වල hash values (fingerprints) හදාගෙන cache එකක save කරගන්නවා.
ඊළඟ වතාවේදී ඒ task එක ආපහු run කරන්න හදනකොට, Gradle මුලින්ම බලනවා, task එකේ inputs හෝ outputs වල කිසියම් වෙනසක් වෙලා තියෙනවද කියලා. කිසිම වෙනසක් වෙලා නැත්නම්, Gradle ඒ task එක 'UP-TO-DATE' කියලා mark කරලා skip කරනවා, ඒක ආපහු run කරන්නේ නැහැ. මේක තමයි Incremental Builds වල හරය!
අපිට මේකෙන් උපරිම ප්රයෝජන ගන්න නම්, අපි custom tasks හදනකොට අපේ task එකේ inputs සහ outputs හරියටම declare කරන්න ඕනේ. එතකොට තමයි Gradle ට මේ up-to-date checks හරියට කරන්න පුළුවන් වෙන්නේ.
උදාහරණයක් විදිහට, text file එකක් process කරන custom task එකක් බලමු:
// build.gradle
task processText {
// Task එකේ input file එක declare කරනවා
inputs.file file('src/main/resources/input.txt')
// Task එකේ output file එක declare කරනවා
outputs.file file('build/processed/output.txt')
doLast {
def inputFile = file('src/main/resources/input.txt')
def outputFile = file('build/processed/output.txt')
// Output directory එක නැත්නම් හදනවා
if (!outputFile.parentFile.exists()) {
outputFile.parentFile.mkdirs()
}
// Input file එකේ content එක Capitalize කරලා output file එකට ලියනවා
def content = inputFile.text.toUpperCase()
outputFile.text = content
println "Processed text from ${inputFile.name} and saved to ${outputFile.name}"
}
}
මේක test කරන්න, src/main/resources/input.txt
කියලා file එකක් හදලා ඒකට මොනවා හරි text එකක් දාන්න. ඊට පස්සේ terminal එකේ gradle processText
කියලා run කරන්න. මුලින්ම task එක run වෙලා output file එක හැදෙයි.
දැන් ආපහු gradle processText
කියලා run කරන්න. ඔයාලට පෙනෙයි task එක UP-TO-DATE කියලා පෙන්වලා run නොවී skip වෙනවා. මොකද input file එක වෙනස් වෙලා නැති නිසා.
දැන් input.txt
file එකේ content එක වෙනස් කරලා ආපහු gradle processText
run කරන්න. එතකොට task එක ආපහු run වෙලා output file එක update වෙනවා. මේකෙන් තමයි Incremental Builds කියන concept එකේ වාසිය ඔයාලට තේරෙන්නේ!
අවසන් වචන
Gradle Build Lifecycle එක තේරුම් ගැනීමෙන්, ඔයාලට පුළුවන් builds වේගවත් කරගන්න, common issues solve කරගන්න, සහ තව effective විදිහට build scripts ලියන්න. Initialization, Configuration, Execution කියන මේ ප්රධාන අදියර තුනයි, Tasks සහ Incremental Builds කියන concepts ටිකයි හරියට තේරුම් ගත්තා නම්, ඔයාලා Gradle master කෙනෙක් වෙන එක එච්චර අමාරු දෙයක් නෙමෙයි.
මේක තනිකරම theory විතරක් නෙමෙයි, practical විදිහට ගොඩක් වටින දෙයක්. අප්පට සිරි! දැන් ඉතින් ඕගොල්ලන්ට තියෙන්නේ මේවා practice කරන එක තමයි. පොඩි project එකක් හදලා මේ concepts apply කරලා බලන්න. ඒකෙන් ඔයාලගේ දැනුම තවත් දියුණු වෙයි.
මේ ගැන ඔයාලට මොනව හරි ප්රශ්න තියෙනවා නම්, පහළින් comment section එකේ අහන්න. අපි උත්තර දෙන්න ලෑස්තියි! තවත් මේ වගේ වැදගත් article එකකින් හම්බවෙමු! පරිස්සමෙන් ඉන්න, code කරන එක Enjoy කරන්න!