פתרון יחסי התלות ב-Gradle

קובצי ה-build מציינים את יחסי התלות הישירים, אבל לכל אחד מהם יכולות להיות יחסי תלות נוספים. יחסי התלות העקיפים האלה גורמים להגדלה מהירה של תרשים התלות הכולל, ולעיתים קרובות יש בהם גרסאות סותרות.

כשהחלקים minor (תכונות חדשות) או patch (תיקוני באגים) משתנים, סביר להניח שהספרייה עדיין תהיה תואמת, ויש סיכוי נמוך יותר שהיא תשפיע על האפליקציה.

לדוגמה, נניח שהאפליקציה שלכם תלויה בספרייה א' ובספרייה ב', שגם הן תלויות בגרסאות שונות של ספרייה ג'.

האפליקציה שלכם תלויה בספרייה א' ובספרייה ב', שגם הן תלויות בגרסאות שונות של ספרייה ג'. Gradle בוחרת את הגרסה העדכנית ביותר של ספרייה C.
איור 1. התנגשות בין גרסאות טרנזיטיביות. Gradle פותר את הבעיה לגרסה החדשה ביותר (כברירת מחדל).

במקרה כזה, Gradle יבחר כברירת מחדל בגרסה החדשה ביותר של ספרייה C, וזה עלול לגרום לבעיות בהדרכה או בסביבת זמן הריצה. בדוגמה הזו, ספריית C מנותבת לגרסה 2.1.1, אבל חשוב לשים לב שספריית A ביקשה את ספריית C בגרסה 1.0.3. החלק העיקרי של מספר הגרסה השתנה, מה שמצביע על שינויים לא תואמים, כמו פונקציות או סוגים שהוסרו. כתוצאה מכך, יכול להיות שתתרחש קריסה של קריאות שבוצעו מספרייה א'.

לאפליקציה יכולות להיות יחסי תלות ישירים שהם גם יחסי תלות טרנזיטיביים.

האפליקציה שלכם תלויה בספרייה א' ובספרייה ג'. ספרייה א' תלויה בגרסה חדשה יותר של ספרייה ג'. Gradle בוחרת את הגרסה העדכנית ביותר של ספרייה C.
איור 2. עוד סכסוך בין גרסאות טרנזיטיביות. במקרה הזה, Gradle פותר את הגרסה הטרנזיטיבית, והאפליקציה רואה את הגרסה החדשה יותר.

במקרה כזה, יחסי תלות טרנזיטיביים חדשים יותר יכולים לשנות את הגרסה שביקשת ישירות באפליקציה.

Gradle בודק את כל הגרסאות האפשריות לכל יחסי התלות בתרשים כדי לקבוע את הגרסה העדכנית ביותר של כל יחסי התלות. אפשר להשתמש במשימות Gradle בסיסיות ובפלאגינים של צד שלישי כדי לקבוע אילו גרסאות של כל יחסי התלות Gradle פתר. השוואת השינויים ברזולוציה הזו היא המפתח להבנת הסיכונים של השדרוג ולצמצום שלהם.

לדוגמה, אפשר להשתמש במשימה dependencies של Gradle על ידי הפעלת ./gradlew app:dependencies כדי להציג עץ של כל יחסי התלות שבהם משתמש מודול האפליקציה. כשמריצים את הבדיקה הזו באפליקציה שמשתמשת בספריות כפי שמוצג באיור 2, מופיע

1: releaseRuntimeClasspath - Runtime classpath of /release.
2: +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0
3: |    +--- ... (omitted for brevity) ...
4: +--- com.sample:library.a:1.2.3
5: |    +--- com.sample:library.c:2.1.1
6: |    |    \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
7: |    \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
8: +--- com.sample:library.c:1.4.1 -> 2.1.1 (*)

בחלק הזה של הדוח מוצגות חלק מהתלות שהתקבלו עבור ההגדרה של releaseRuntimeClasspath.

בכל פעם שמופיע הערך -> בדוח יחסי התלות, מבקש (האפליקציה או ספרייה אחרת) משתמש בגרסה של יחסי התלות שלא צפויה לו. ברוב המקרים, זה לא גורם לבעיות, כי רוב הספריות נכתבות עם תאימות לאחור. עם זאת, יכול להיות שספריות מסוימות יבצעו שינויים לא תואמים, והדוח הזה יכול לעזור לכם לקבוע מאיפה מגיעות בעיות חדשות בהתנהגות האפליקציה.

פרטים נוספים על השימוש בדיווח על יחסי התלות ב-Gradle זמינים במאמר הצגה וניפוי באגים של יחסי התלות.

אפשר לציין את הגרסאות המבוקשות ישירות, בקטלוג גרסאות או ב-Bill of Materials (BOM).

פתרון ישיר של הגדרת גרסת ה-API

הגרסאות של יחסי התלות שציינתם הופכות למועמדות לפתרון בעיות בגרסאות.

לדוגמה, כדי לבקש את הגרסה 1.7.3 של ספריית androidx.compose.ui:ui בתור יחסי תלות ב-app/build.gradle.kts:

dependencies {
    implementation("androidx.compose.ui:ui:1.7.3")
}

הגרסה 1.7.3 הופכת לגרסה מועמדת. Gradle פותר את הגרסה העדכנית ביותר מבין 1.7.3 וגרסאות אחרות של אותה ספרייה שנדרשות על ידי יחסי תלות טרנזיטיביים.

רזולוציית קטלוג הגרסאות

קטלוגים של גרסאות מגדירים משתנים למעקב אחרי הגרסה של יחסי התלות שבהם נעשה שימוש באפליקציה. אם משתמשים במשתנה ממאגר הגרסאות, יחסי התלות שצוינו של המשתנה הזה מתווספים לאפשרויות לפתרון הגרסה. המערכת מתעלמת ממשתנים שלא בשימוש בקטלוג הגרסאות.

לדוגמה, כדי לציין את גרסת 1.7.3 של androidx.compose.ui:ui כיחס תלות בקובץ gradle/libs.versions.toml:

[versions]
ui = "1.7.3"

[libraries]
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }

כך מגדירים משתנה בשם libs.androidx.compose.ui שמייצג את הספרייה. הגרסה הזו לא נחשבת כמועמדת, אלא אם משתמשים במשתנה הזה כדי לציין יחסי תלות.

כדי לבקש את הספרייה ואת הגרסה שלה ב-app/build.gradle.kts:

dependencies {
    implementation(libs.androidx.compose.ui)
}

Gradle פותר את הבעיה באותו אופן שבו הוא פותר אותה עבור מפרט ישיר.

רזולוציית מפרט החומרים (BoM)

הגרסאות של כל הספריות שמופיעות ב-BOM הופכות למועמדות לפתרון בעיות בגרסאות. חשוב לזכור שנעשה שימוש בספריות כיחסי תלות רק אם הן מצוינות כיחסי תלות ישירים או עקיפים. המערכת מתעלמת מספריות אחרות ב-BOM.

גרסאות ה-BOM משפיעות על יחסי התלות הישירים וגם על כל יחסי התלות הטרנזיטיביים שמופיעים ב-BOM.

לדוגמה, מציינים BOM כיחס תלות מסוג platform בקובץ app/build.gradle.kts:

dependencies {
    implementation(platform("androidx.compose:compose-bom:2024.10.00"))
    implementation("androidx.compose.ui:ui")
}

אין צורך לציין הגדרת גרסה של ספריות שרוצים להשתמש בהן כיחסי תלות. הגרסה המבוקשת מגיעה מ-BOM.

הערה: אפשר גם להשתמש בקטלוג גרסאות כדי ליצור משתנים ל-BOM ולספריות. משמיטים את מספרי הגרסאות בקטלוג הגרסאות של ספריות שמופיעות בקשרי תלות ב-BOM.

לדוגמה, קטלוג הגרסאות מכיל את ה-BOM ומספר הגרסה שלו, אבל לא מצוין בו גרסת ספרייה שאליה מפנים מה-BOM:

[versions]
composeBom = "2024.10.00"

[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }

קובץ app/build.gradle.kts מפנה ל-BOM ולספריות באמצעות המשתנים שמוגדרים בקטלוג הגרסאות:

dependencies {
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.compose.ui)
}

הגרסה של הספרייה שצוינה ב-BOM הופכת למועמדת לפתרון של Gradle. בנוסף, כל גרסאות הספריות האחרות שצוינו ב-BOM הופכות לגרסאות מועמדות, גם אם אתם משתמשים בהן ישירות כיחסי תלות וגם אם לא.

לדוגמה, נניח שב-BOM מצוינות גרסאות לספריות A,‏ B ו-C. האפליקציה שלכם רוצה להשתמש ישירות בספרייה A כספריית תלות, וגם בספרייה D. ספרייה D משתמשת בספרייה B כיחס תלות. אף אחד לא משתמש בספרייה C.

BOM כולל גרסאות לספריות A,‏ B ו-C. האפליקציה משתמשת בספריות A ו-D כיחסי תלות. ספרייה D משתמשת בספרייה B כיחס תלות. לא נעשה שימוש בספרייה C באופן ישיר או עקיף באפליקציה הזו.
איור 3. תרחיש BOM.

הספריות א', ב' ו-ד' הן יחסי תלות באפליקציה. המערכת מתעלמת מהספרייה ג'. Gradle משתמש בגרסאות של A ו-B שצוינו ב-BOM כאפשרויות, גם אם לא ציינתם ישירות את ספריית B כחילופין.

אם ספרייה D ביקשה גרסה של ספרייה B שקדמה ל-2.0.1, Gradle יפתור את הבעיה ויגדיר את הגרסה כ-2.0.1. אם ספרייה D ביקשה גרסה מתקדמת יותר של ספרייה B, Gradle יפתור לגרסה הזו.