Kotlin DSL ve buildSrc ile Gradle Görev Yönetimi Optimizasyonu
Yayınlandı: 11 July 2025
Derleme görevlerinin otomasyonu her yazılım projesinde kritik öneme sahiptir ve Gradle, Kotlin DSL’iyle muhteşem bir esneklik sunar. Konuşmamız sırasında, test raporları gibi şeyler için özellikle derleme mantığını nasıl merkezi hale getirip yeniden kullanabileceğimizi keşfettik.allprojects, buildSrc, ve özel görevler Kotlin’de.
allprojects` ve subprojects bloklarını kullanarak yapılandırmaları merkezileştirin
Bloklar`allprojects` et `subprojects`senin içinde`build.gradle.kts`Kökler, çok modüllü projenizde ortak konfigürasyonları uygulamada temel bir rol oynar.
*allprojects { … }`Yapılandırmayı uygulakök proje ve tüm alt projeleri. Tanım yapmak için ideal`group, bir`version`, veya bazı`repositories`ortak. *subprojects { … }`Konfigürasyonu uygulaYalnızca alt-projelere, kök projeyi hariç tutarak. Modüllere özel eklentiler uygulamak için idealdir (örnek`java ou kotlin-jvm) veya kitaplığınızdaki ortak bağımlılıklar.
İşte bir örnek gösterimi:
// build.gradle.kts (projet racine)
plugins {
base // Appliqué au projet racine
}
allprojects {
group = "com.example"
version = "1.0.0"
repositories {
mavenCentral()
}
tasks.withType<org.gradle.api.tasks.testing.Test> {
useJUnitPlatform() // Configuration commune des tests pour tous les projets
}
}
subprojects {
apply(plugin = "java")
apply(plugin = "org.jetbrains.kotlin.jvm")
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}
}
\==`buildSrc`Derleme mantığının İsviçre çakısı
Görevlerinizin mantığı karmaşık hale geldiğinde ya da yeniden kullanılması gerektiğinde,buildSrctercih eden çözüm. Bu özel bir Gradle modülüdür, ana derleme scriptlerinden önce derlenir ve sınıflarını tüm derleme class path’ine erişilebilir kılar.
Neden kullanmak`buildSrc`Görevler için?
-
yeniden kullanabilirlikBir görev tanımlanmış innerhalb`buildSrc`herhangi bir derleme projesine uygulanabilir
-
Organizasyon: Derleme kodunu merkezileştirerek daha temiz ve sürdürülebilir hâle getirir.
-
Tip Güvenliği ve Otomatik Tamamlama: Kotlin kodu içinde`buildSrc`derlenir, hataları tespit ederek ve IDE’nizin otomatik tamamlamasını sağlayarak geliştirme deneyimini iyileştirir.
\=== Akış Diyagramı`buildSrc`(Kod PlantUML)
Bu diyagramı oluşturmak için aşağıdaki kodu kopyalayıp PlantUML destekleyen bir araca yapıştırın.
[source,plantuml]
@startuml skinparam handwritten true skinparam tekrenkli doğru
dikdörtgen "Gradle Build Process" { bileşen "buildSrc" olarak BS { dosya "MyCustomTask.kt" olarak T file "MyConventionPlugin.kt" as P } bileşen "Root Project" olarak RP bileşen "Subproject A" olarak SA bileşen "Subproject B" olarak SB }
tanımlanır P --\> RP : "is applied to P -→ SA : "is applied to P --\> SB : "uygulanır
RP --|\> SA : "contains RP --|\> SB : "içerir
RP -up→ BS : "depends on (for build logic) SA -up→ BS : "depends on (for build logic) SB -up→ BS : "depends on (for build logic)
not T’nin sağında Özel görev sınıfları (örnek: OpenTestReportTask) end note
P’nin sağ notu Konvansiyon eklentileri görevleri kaydedenler son not
@enduml
Soyut Rapor Görevlerinin Oluşturulması
Test raporlarını yönetmek için, soyut görev sınıfı kullanarak modüler bir yaklaşım tasarladık içinde`buildSrc`. İhtiyaçlarınıza göre, bu sınıf devralabilir`DefaultTask` ou de Exec.
\=== Soyut Görev`OpenTestReportTask`(devralan`DefaultTask`)
Bu yaklaşım, dosya sistemi veya diğer Gradle API’leriyle etkileşim kuran özel Kotlin mantığına ihtiyacınız varsa ve ardından dış bir komut çalıştırmanız gerekiyorsa önerilir.
[source,kotlin]
paket com.yourpackage
import org.gradle.api.DefaultTask import org.gradle.api.tasks.Input import org.gradle.api.tasks.TaskAction import java.io.File
abstract class OpenTestReportTask : DefaultTask() {
----
init {
group = "verification"
description = "Opens a test report in Firefox."
dependsOn("check") // Assure que les rapports sont générés
}
@get:Input
abstract var reportPath: String
@TaskAction
fun openReport() {
val separator = File.separator
val reportFile = project.layout.projectDirectory.asFile.toPath()
.resolve(reportPath.replace("/", separator))
.toAbsolutePath()
.toFile()
if (!reportFile.exists()) {
logger.warn("Report file does not exist: $reportFile. Ensure 'check' ran.")
return
}
project.exec {
commandLine("firefox", "--new-tab", reportFile.absolutePath)
}
logger.lifecycle("Opened test report: ${reportFile.absolutePath}")
}
----
## }
Somut Uygulamalar
Bu sınıflar soyut görevden kalıtım alır ve raporun belirli yolunu tanımlar.
[source,kotlin]
package com.yourpackage
soyut sınıf ReportUnitTestsTask : OpenTestReportTask() init { Birim test raporunu Firefox'ta açar. reportPath = "build/reports/tests/test/index.html } }
paket com.yourpackage
abstract class ReportFunctionalTestsTask : OpenTestReportTask() {
init {
description = "Fonksiyonel test raporunu Firefox'ta açar."
reportPath = "build/reports/tests/functionalTest/index.html"
}
}
Görev Kayıtları
İçinde`build.gradle.kts`kök projeniz:
[source,kotlin]
tasks.register\<com.yourpackage.ReportUnitTestsTask\>("reportTests") {} tasks.register\<com.yourpackage.ReportFunctionalTestsTask\>("reportFunctionalTests") {}
\=== Rapor Görevleri UML Diyagramı (Code PlantUML)
Aşağıdaki kodu kopyalayıp PlantUML'i destekleyen bir araca yapıştırın.
## [source,plantuml]
@startuml skinparam elyazısı true skinparam monochrome true
soyut sınıf DefaultTask { }
soyut sınıf Exec { \+ commandLine(args: String...) \+ exec() }
abstract class OpenTestReportTask extends DefaultTask { + grup: String = "doğrulama + açıklama: String \+ dependsOn("check") \+ abstract reportPath: String \+ openReport() : void }
Soyut sınıf AbstractJbakeExecTask Exec'i genişletir. \+ group: String = "verification \> note:` No additional output needed beyond this line. \+ dependsOn("check") \+ abstract reportRelativePath: String \+ exec() : void }
class ReportUnitTestsTask extends OpenTestReportTask { + reportPath: String = "build/reports/tests/test/index.html }
class ReportFunctionalTestsTask extends OpenTestReportTask { \+ reportPath: String = "build/reports/tests/functionalTest/index.html }
sınıf ReportJbakeTestsTask AbstractJbakeExecTask'ı genişletir { \+ reportRelativePath: String = "build/reports/tests/test/index.html }
class ReportJbakeFunctionalTestsTask extends AbstractJbakeExecTask { \+ reportRelativePath: String = "build/reports/tests/functionalTest/index.html }
OpenTestReportTask <-- ReportUnitTestsTask OpenTestReportTask <-- ReportFunctionalTestsTask
AbstractJbakeExecTask <-- ReportJbakeTestsTask AbstractJbakeExecTask \<-- ReportJbakeFunctionalTestsTask
DefaultTask \<|-- OpenTestReportTask Exec \<|-- AbstractJbakeExecTask DefaultTask \<|-- Exec
## @enduml
Soyut görev`AbstractJbakeExecTask`(devralan`Exec`)
Eğer görevinizin temel amacı dışarıdan bir komutu değişken argümanlarla çalıştırmaksa, doğrudan miras alın.**Exec**daha doğrudur.
## [source,kotlin]
package com.yourpackage
import org.gradle.api.tasks.Exec import org.gradle.api.tasks.Input import java.io.File
abstract class AbstractJbakeExecTask : Exec() {
init {
group = "verification"
description = "Opens a Jbake project report in Firefox."
dependsOn("check")
}
@get:Input
abstract var reportRelativePath: String
override fun exec() {
val separator = File.separator
val reportFile = project.layout.projectDirectory.asFile.toPath()
.resolve(reportRelativePath.replace("/", separator))
.toAbsolutePath()
.toFile()
if (!reportFile.exists()) {
logger.warn("Report file does not exist: $reportFile. Ensure 'check' ran.")
return
}
commandLine("firefox", "--new-tab", reportFile.absolutePath)
logger.lifecycle("Attempting to open report: ${reportFile.absolutePath}")
super.exec() // Appelle la méthode exec() de la super-classe Exec
}
}
\=== Somut Uygulamalar için`Exec`
[source,kotlin]
package com.yourpackage
abstract class ReportJbakeTestsTask : AbstractJbakeExecTask() init { description = "Jbake birim testi raporunu Firefox’ta açar. reportRelativePath = "build/reports/tests/test/index.html } }
paket com.yourpackage
abstract class ReportJbakeFunctionalTestsTask : AbstractJbakeExecTask() { init { description = "İşlevsel test raporunu Firefox’ta açar." reportRelativePath = "build/reports/tests/functionalTest/index.html" } }
Görevlerin Görünürlüğü`buildSrc`
Tanımladığınız görevler ve sınıflar içindebuildSrcoluralar :
(blank)Görünür ve KullanılabilirAna buildinizdeki tüm projelerde (kök ve alt-proje). Bu yüzden kullanabilirsiniz.com.yourpackage.ReportUnitTestsTask`içinde`allprojects { … }. (No text)Çalıştırılamayanlardoğrudan buildSrc görevleri olarak (örnek olarak,gradle :buildSrc:reportTests`Özel olarak kaydedilmediği takdirde çalışmazdı`buildSrc/build.gradle.kts)`buildSrc`Bir derleme zamanı modülüdür, ana derleme görevleri için yürütülebilir bir uygulama modülü değildir.
\=== Testler için bir rapor çalıştır`buildSrc`kendi
Si buildSrc`Kendi testlerini yürütür ve raporlar üretir, bir rapor görevi doğrudan kaydedebilirsiniz.`buildSrc/build.gradle.kts(empty)
[source,kotlin]
plugins { kotlin-jvm }
depolar { mavenCentral() }
tasks.withType\<Test\> { useJUnitPlatform() reports.html.outputLocation.set(layout.buildDirectory.dir("reports/tests")) }
import com.yourpackage.ReportJbakeTestsTask // Görev sınıfınızı içe aktarın
tasks.register<ReportJbakeTestsTask>("reportBuildSrcTests") { // Yol sınıf içinde zaten tanımlanmıştır ve buildSrc raporlarına işaret edecektir }
Sonra şu komutu çalıştırabilirsiniz :`./gradlew :buildSrc:test`ardından`./gradlew :buildSrc:reportBuildSrcTests`.
Bu uygulamaları benimsederek, sadece güçlü değil, aynı zamanda harika derecede modüler, sürdürülebilir ve anlaşılması kolay Kotlin DSL Gradle derleme sistemleri oluşturacaksınız.