Điều 5 : Cuộc phiêu lưu của `libs.versions.toml` Chia sẻ
Đăng ngày 27 September 2025
Đối tượng mục tiêu: Các nhà phát triển Gradle gặp khó khăn trong việc quản lý phụ thuộc trong các dự án đa build.
Giới thiệu
Trong hành trình tạo ra plugin`site-baker`, chúng tôi đã cố gắng duy trì một cơ sở mã sạch và tập trung. Một trong những thực hành tốt nhất trong hệ sinh thái Gradle hiện đại là việc sử dụngdanh mục phiên bản(libs.versions.toml) để quản lý các phụ thuộc. Nhưng điều gì sẽ xảy ra khi dự án của bạn trở nên phức tạp hơn, chẳng hạn như mộtxây dựng compositenơi một bản build độc lập (plugin của chúng tôi) cần phải được bao gồm trong một bản build khác?
Đó là lúc cuộc phiêu lưu của chúng tôi rẽ sang một hướng bất ngờ. Chúng tôi muốn plugin của mình`site-baker`, chính nó cũng là một dự án Gradle, sử dụng cùng một catalogue phiên bản với dự án chính của chúng tôi. Bài viết này, bài thứ năm trong chuỗi bài của chúng tôi, kể về cách chúng tôi đã giải quyết thử thách này.
1. Danh mục Phiên bản (libs.versions.toml): Một lời nhắc lại
Tệp`gradle/libs.versions.toml`là một tính năng của Gradle cho phép tập trung hóa các phiên bản và tọa độ của các phụ thuộc trong dự án của bạn. Nó thường được cấu trúc thành bốn phần:
-
[versions]: Định nghĩa các bí danh cho số phiên bản (ví dụ:`kotlin = "1.9.20"`). -
[libraries]: Định nghĩa các bí danh cho các phụ thuộc đầy đủ, sử dụng các phiên bản đã được định nghĩa ở trên (ví dụ:`kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }`). -
[bundles]: Nhóm nhiều thư viện dưới một bí danh duy nhất (ví dụ:`jackson = ["jackson-core", "jackson-databind"]`). -
[plugins]: Định nghĩa các bí danh cho các plugin Gradle.
Một khi được định nghĩa, Gradle sẽ tự động tạo các trình truy cập có kiểu (typed accessors), điều này khiến cho`build.gradle.kts`dễ đọc hơn nhiều:
dependencies {
// Au lieu de : implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.20")
implementation(libs.kotlin.stdlib)
}
Luồng dữ liệu của danh mục phiên bản
@startuml !theme plain file "gradle/libs.versions.toml" as Toml node "Gradle" as G file "build.gradle.kts" as BuildScript Toml --> G : "được đọc bởi" G --> BuildScript : "tạo ra bộ truy cập `libs`" BuildScript -> G : "sử dụng `libs` để khai báo các phụ thuộc" @enduml
2. Vấn đề: Một Build Composite và Hai Thế giới Riêng biệt
Cấu trúc dự án của chúng tôi là mộtxây dựng composite. Dự án chính (thymeleaf.cheroliv.com) bao gồm dự án plugin (site-baker) thông qua lệnh`includeBuild("site-baker")`trong tệp`settings.gradle.kts`Vui lòng cung cấp văn bản tiếng Pháp bạn muốn dịch.
Cấu trúc của bản build composite
@startuml
!theme plain
package "Dự án Chính" {
file "settings.gradle.kts" as MainSettings
folder "gradle" {
file "libs.versions.toml" as MainToml
}
}
package "Plugin `site-baker` (Build Inclus)" {
file "settings.gradle.kts" as PluginSettings
file "build.gradle.kts" as PluginBuild
}
MainSettings -> PluginSettings : `includeBuild("site-baker")`
PluginBuild ..> MainToml : **Voulait accéder à...**
note right on link: ...nhưng không thể!
@enduml
Vấn đề là, theo mặc định, một build được bao gồm là mộtthế giới tách biệt. Nó có cấu hình riêng, vòng đời riêng, và nó không nhìn thấy tệp`libs.versions.toml`của build chính. Chúng tôi`plugin/build.gradle.kts`đang cố gắng sử dụng`libs.plugins.kotlin.jvm`, nhưng đối tượng`libs`không được tạo từ tệp TOML đúng, gây ra các lỗi build.
3. Giải pháp: Chia sẻ việc Giải quyết Phụ thuộc
Sau nhiều lần tìm kiếm, giải pháp hóa ra là một tính năng của Gradle được thiết kế chính xác cho trường hợp này:`dependencyResolutionManagement`Vì bạn không cung cấp nội dung cần dịch, vui lòng gửi văn bản tiếng Pháp bạn muốn chuyển sang tiếng Việt.
Trong tệp`settings.gradle.kts` du bản build bao gồm(site-baker/settings.gradle.kts), chúng ta có thể cho Gradle biết nơi tìm danh mục phiên bản cần sử dụng.
Đây là cấu hình mà chúng tôi đã thêm vào`site-baker/settings.gradle.kts`Vui lòng cung cấp văn bản cần dịch.
// site-baker/settings.gradle.kts
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
gradlePluginPortal()
mavenCentral()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
rootProject.name = "site-baker"
include("plugin")
Hãy cùng phân tích phần quan trọng:
versionCatalogs {
create("libs") { // Crée un catalogue nommé 'libs'
from(files("../gradle/libs.versions.toml")) // En utilisant ce fichier
}
}
-
create("libs"): Chúng tôi khai báo một danh mục phiên bản sẽ có thể truy cập được thông qua bí danh`libs`Vui lòng cung cấp văn bản cần dịch. -
from(files("../gradle/libs.versions.toml")): Đó là phép màu. Chúng ta chỉ cho Gradle biết rằng nguồn của danh mục này là tệp TOML nằm trong thư mục`gradle` du dự án cha(../).
Với cấu hình này, bản build`site-baker`giờ đã biết rằng nó phải sử dụng danh mục phiên bản của dự án chính. Đối tượng`libs`được tạo chính xác, và các phụ thuộc được giải quyết như dự kiến.
4. Kết luận: Sức mạnh của các Bản Build Composite được Quản lý Tốt
Trải nghiệm này đã mang lại nhiều bài học quý giá. Danh mục phiên bản (version catalogue) là một công cụ tuyệt vời cho khả năng bảo trì, nhưng hành vi của nó trong các kịch bản build tổng hợp không phải lúc nào cũng trực quan.
Chìa khóa là hãy nhớ rằng một build bao gồm vẫn là một build độc lập. Để chia sẻ các cấu hình như danh mục phiên bản, cần sử dụng các cơ chế rõ ràng được Gradle cung cấp, chẳng hạn như khối`dependencyResolutionManagement`trong`settings.gradle.kts`Vui lòng cung cấp văn bản tiếng Pháp cần dịch.
Bằng cách giải quyết vấn đề này, chúng ta không chỉ làm cho bản build sạch hơn, mà còn tăng cường tính nhất quán cho dự án bằng cách đảm bảo rằng plugin và dự án chính chia sẻ một nguồn sự thật duy nhất cho các phụ thuộc của chúng.
Trong bài viết tiếp theo, chúng ta sẽ tiếp tục làm phong phú plugin của mình bằng cách thêm vào các tác vụ phức tạp hơn, giờ đây khi việc quản lý phụ thuộc của chúng ta đã vững chắc và tập trung.