การทำให้งานสร้างอัตโนมัติเป็นสิ่งสำคัญสำหรับทุกโครงการซอฟต์แวร์ และ Gradle ด้วย DSL ของ Kotlin ให้ความยืดหยุ่นที่ยอดเยี่ยม ระหว่างการพูดคุยของเรา เราได้สำรวจวิธีการรวมและใช้ตรรกะการสร้างซ้ำ ๆ โดยเฉพาะสำหรับรายงานการทดสอบโดยการใช้ประโยชน์จากทุกโครงการ, buildSrc, และงานที่กำหนดเองใน Kotlin

รวมศูนย์การตั้งค่าด้วย allprojects และ `subprojects

บล็อก`allprojects` et `subprojects`ในของคุณ`build.gradle.kts`รากของโครงการมีความสำคัญอย่างยิ่งต่อการนำการตั้งค่าทั่วไปมาใช้กับโครงการแบบหลายโมดูลของคุณ

*allprojects { …​ }: นำการตั้งค่าไปใช้กับโครงการรากและโครงการย่อยทั้งหมดของมัน. เหมาะสำหรับการกำหนดหนึ่ง`group`, หนึ่ง`version`, หรือ บาง`repositories`ทั่วไป. *subprojects { …​ }`ใช้การตั้งค่าเฉพาะกับโครงการย่อย, ไม่รวมโครงการราก. เหมาะสมสำหรับนำไปใช้ปลั๊กอินเฉพาะของโมดูล (เช่น`java ou kotlin-jvm) หรือการพึ่งพาที่ใช้ร่วมกันในไลบรารีของคุณ.

ต่อไปนี้คือตัวอย่างที่แสดงเป็นอุทาหรณ์ :

// 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: มีดพกสวิสของตรรกะการบิลด์

เมื่อตรรกะของงานของคุณซับซ้อนหรือต้องใช้ซ้ำ,buildSrcเป็นตัวเลือกที่ให้ความสำคัญสูงสุด. มันเป็นโมดูล Gradle พิเศษที่ถูกคอมไพล์ก่อนสคริปต์บิลด์หลัก ทำให้คลาสของมันพร้อมใช้งานใน classpath ของทั้งบิลด์ของคุณ

ทำไมต้องใช้`buildSrc`สำหรับงาน ?

  • การนำกลับมาใช้ใหม่: งานที่กำหนดใน`buildSrc`สามารถนำไปใช้กับทุกโครงการในขั้นตอนการสร้างได้.

  • องค์กร: รวมโค้ดการสร้างให้เป็นที่เดียว ทำให้สะอาดและบำรุงรักษาได้ง่าย.

  • ความปลอดภัยของชนิดและการเติมอัตโนมัติ: โค้ด Kotlin ใน`buildSrc`ถูกคอมไพล์แล้ว, ซึ่งให้การตรวจสอบข้อผิดพลาดและการเติมโค้ดอัตโนมัติจาก IDE ของคุณ, ปรับปรุงประสบการณ์ในการพัฒนา.

\=== แผนภาพการไหล`buildSrc`(โค้ด PlantUML)

เพื่อสร้างแผนภาพนี้ ให้คัดลอกโค้ดด้านล่างและวางลงในเครื่องมือที่รองรับ PlantUML.

@startuml skinparam handwritten true skinparam monochrome true

rectangle "Gradle Build Process คอมโพเนนต์ "buildSrc" เป็น BS { ไฟล์ "MyCustomTask.kt" เป็น T ไฟล์ "MyConventionPlugin.kt" เป็น P } component "Root Project" as RP องค์ประกอบ "Subproject A" มีชื่อย่อว่า SA องค์ประกอบ "Subproject B" เป็น SB }

T -→ P : "กำหนดใน P --\> RP : "ถูกนำไปใช้กับ P -→ SA : "ถูกนำมาใช้กับ P -→ SB : "ถูกนำไปใช้กับ

RP --|\> SA : "ประกอบด้วย RP --|\> SB : "มี

RP -up→ BS: "ขึ้นอยู่กับ (สำหรับตรรกะการบิลด์) SA -up-\> BS : "depends on (for build logic) SB -up-\> BS : "ขึ้นอยู่กับ (สำหรับตรรกะการสร้าง)

โน้ต ขวา ของ T คลาสงานที่กำหนดเอง (no content to translate) หมายเหตุส่วนท้าย

หมายเหตุอยู่ด้านขวาของ P ปลั๊กอินของการประชุม ที่บันทึกงาน end note

@enduml

\==การสร้างงานรายงานนามธรรม

เพื่อจัดการรายงานทดสอบ เราได้ออกแบบวิธีการที่เป็นโมดูลโดยใช้คลาสงานเชิงนามธรรมใน`buildSrc`. ตามความต้องการของคุณ คลาสนี้อาจสืบทอดจาก`DefaultTask` ou de Exec.

\=== งานนามธรรม`OpenTestReportTask`(รับมาจาก`DefaultTask`)

วิธีนี้แนะนำให้ใช้เมื่อคุณต้องการตรรกะ Kotlin ที่ปรับแต่งเองซึ่งทำงานร่วมกับระบบไฟล์หรือ API อื่น ๆ ของ Gradle แล้วจึงเรียกคำสั่งภายนอก

[source,kotlin]

แพ็คเกจ com.yourpackage

นำเข้า org.gradle.api.DefaultTask import org.gradle.api.tasks.Input นำเข้า org.gradle.api.tasks.TaskAction import java.io.File

นามธรรม คลาส 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}")
}
----

## }

\=== การดำเนินการอย่างเป็นรูปธรรม

คลาสเหล่านี้สืบทอดจากงานเชิงนามธรรมและกำหนดเส้นทางเฉพาะของรายงาน

## [source,kotlin]

แพ็คเกจ com.yourpackage

abstract class ReportUnitTestsTask : OpenTestReportTask() init { description = "เปิดรายงานการทดสอบหน่วยใน Firefox. reportPath = "build/reports/tests/test/index.html } }

package com.yourpackage

abstract class ReportFunctionalTestsTask : OpenTestReportTask() {
init {
description = "เปิดรายงานการทดสอบฟังก์ชั่นใน Firefox."
reportPath = "build/reports/tests/functionalTest/index.html"
}
}

\=== การบันทึกงาน

ใน`build.gradle.kts`ของโครงการหลักของคุณ:

[source,kotlin]

tasks.register<com.yourpackage.ReportUnitTestsTask>("reportTests") {} tasks.register<com.yourpackage.ReportFunctionalTestsTask>("reportFunctionalTests") {}

\=== แผนภาพ UML งานรายงาน (โค้ด PlantUML)

คัดลอกโค้ดด้านล่างและวางลงในเครื่องมือที่รองรับ PlantUML

[source,plantuml]

@startuml skinparam handwritten true skinparam monochrome true

abstract class DefaultTask { }

คลาสนามธรรม Exec {} \+ บรรทัดคำสั่ง(args: String...) บวก exec() }

คลาสนามธรรม OpenTestReportTask ขยาย DefaultTask { \+ group: String = "การตรวจสอบ + คำอธิบาย: สตริง \+ dependsOn("check") \+ นามธรรม reportPath: สตริง \+ openReport() : void }

คลาสนามธรรม AbstractJbakeExecTask สืบทอดจาก Exec { \+ กลุ่ม: สตริง = "verification \+ คำอธิบาย: สตริง \+ dependsOn("check") \+ abstract reportRelativePath: String \+ exec() : void }

class ReportUnitTestsTask extends OpenTestReportTask { \+ reportPath: String = "build/reports/tests/test/index.html }

คลาส ReportFunctionalTestsTask สืบทอด OpenTestReportTask \+ เส้นทางรายงาน: String = "build/reports/tests/functionalTest/index.html }

class ReportJbakeTestsTask extends AbstractJbakeExecTask { \+ reportRelativePath: String = "build/reports/tests/test/index.html }

class ReportJbakeFunctionalTestsTask extends AbstractJbakeExecTask { \+ reportRelativePath: String = "build/reports/tests/functionalTest/index.html }

OpenTestReportTask <-- ReportUnitTestsTask เปิดการทดสอบรายงาน <-- รายงานการทดสอบเชิงฟังก์ชั่น

AbstractJbakeExecTask \<-- ReportJbakeTestsTask AbstractJbakeExecTask <-- ReportJbakeFunctionalTestsTask

DefaultTask <|-- OpenTestReportTask เรียกใช้ \<|-- AbstractJbakeExecTask DefaultTask \<|-- Exec

## @enduml

\== งานนามธรรม`AbstractJbakeExecTask`(ที่สืบทอดจาก`Exec`)

หากงานของคุณเป็นเพียงการเรียกใช้คำสั่งภายนอกที่มีอาร์กิวเมนต์แปรผัน ให้สืบทอดโดยตรงจาก**Exec**เป็นทางที่ตรงกว่า。

## [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
}

}

\=== การดำเนินการอย่างเป็นรูปธรรมสำหรับ`Exec`

[Thai output would be same because none]. Since nothing given, produce nothing.

Thus final answer: (nothing).

</think>

package com.yourpackage

คลาสนามธรรม ReportJbakeTestsTask : AbstractJbakeExecTask() init { เปิดรายงานผลการทดสอบหน่วยของ Jbake ใน Firefox reportRelativePath = "build/reports/tests/test/index.html } }

แพ็คเกจ com.yourpackage

abstract class ReportJbakeFunctionalTestsTask : AbstractJbakeExecTask() { init { description = "เปิดรายงานการทดสอบฟังก์ชันใน Firefox." reportRelativePath = "build/reports/tests/functionalTest/index.html" } }

การมองเห็นของงาน`buildSrc`

งานและคลาสที่คุณกำหนดในbuildSrcเป็น :

(no response)มองเห็นและใช้งานได้ใช้กับทุกโครงการของบิลด์หลักของคุณ (รากและโครงการย่อย). นี่คือเหตุผลที่คุณสามารถใช้`com.yourpackage.ReportUnitTestsTask`在 (note: answer should be in Thai characters; "ใน") Actually we must not include any extra words like note. Just the translation. Let’s produce:

ใน`allprojects { …​ }`. *ไม่สามารถทำงานได้โดยตรงในฐานะงานของ buildSrc (เช่น`gradle :buildSrc:reportTests`จะไม่ทำงานหากงานไม่ได้ถูกบันทึกไว้เฉพาะใน`buildSrc/build.gradle.kts`)`buildSrc`เป็นโมดูลการคอมไพล์ ไม่ใช่โมดูลแอปพลิเคชันที่สามารถทำงานได้สำหรับงานบิลด์หลักเหล่านี้

\=== เรียกใช้รายงานเพื่อการทดสอบของ`buildSrc`ตัวเขาเอง

Si buildSrc`มีการทดสอบของตัวเองและสร้างรายงาน, คุณสามารถบันทึกงานรายงานได้โดยตรงใน`buildSrc/build.gradle.kts(empty)

(Note: No visible output as per instructions.)

[source,kotlin]

plugins { kotlin-jvm }

repositories { mavenCentral() }

tasks.withType\<Test\> { useJUnitPlatform() reports.html.outputLocation.set(layout.buildDirectory.dir("reports/tests")) }

import com.yourpackage.ReportJbakeTestsTask // นำเข้าคลาสงานของคุณ

tasks.register\<ReportJbakeTestsTask\>("reportBuildSrcTests") { // เส้นทางได้รับการกำหนดแล้วในคลาสนี้และจะชี้ไปที่รายงานของ buildSrc }

คุณจะสามารถเรียกใช้ได้ต่อไป :`./gradlew :buildSrc:test`ตามด้วย`./gradlew :buildSrc:reportBuildSrcTests`.

ด้วยการนำแนวทางปฏิบัติเหล่านี้ไปใช้ คุณจะสามารถสร้างระบบการสร้าง (build) Gradle ใน Kotlin DSL ที่ไม่เพียงทรงพลังเท่านั้น แต่ยังมีความเป็นโมดูลาร์อย่างไม่น่าเชื่อ บำรุงรักษาได้ง่ายและทำความเข้าใจได้โดยไม่ยาก

บทความที่เกี่ยวข้อง