תלויות

יעד A תלוי ב יעד B אם נדרש B A בזמן build או ביצוע. היחס התלוי ב- גורם לתרשים אציקלי מכוון (DAG) מעל יעדים, והוא נקרא תרשים תלות.

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

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

יחסי תלות בפועל ומוצהרים

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

למטרה X יש תלויות מוצהרות על היעד Y, אם יש יתרון תלוי מ-X ל-Y בחבילה X.

בבניינים נכונים, התרשים של יחסי התלות בפועל A חייב להיות תרשים משנה של התרשים של יחסי תלות מוצהרים D. כלומר, כל צמד של צומת שמחובר ישירות ל-x --> y ב-A צריך להיות מחובר ישירות ב-D. אפשר לומר ש-D הוא הערכה יתר של A.

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

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

אין צורך (ולא כדאי) לציין את כל הנתונים המיובאים באופן עקיף, גם אם הם נדרשים על ידי A בזמן הביצוע.

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

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

1. יחסי התלות המוצהרים תואמים לתלויות בפועל

בהתחלה הכול עובד. הקוד בחבילה a משתמש בקוד בחבילה b. הקוד בחבילה b משתמש בקוד בחבילה c, כך ש-a תלוי בטרמינל ב-c.

a/BUILD b/BUILD
rule(
    name = "a",
    srcs = "a.in",
    deps = "//b:b",
)
      
rule(
    name = "b",
    srcs = "b.in",
    deps = "//c:c",
)
      
a / a.in b / b.in
import b;
b.foo();
    
import c;
function foo() {
  c.bar();
}
      
תרשים תלות של הצהרה עם חיצים שמחברים את a, b ו-c
תרשים תלותהצהרת
תרשים תלות בפועל שתואם את תרשים התלות המוצהר עם חיצים שמחברים את a, b ו-c
ממש תרשים תלות

יחסי התלות המוצהרים מקיפים את יחסי התלות בפועל. הכול בסדר.

2. הוספת תלות לא מוצהרת

סכנת הסתרה נוצרת כאשר מישהו מוסיף קוד ל-a, שיוצר תלות אמיתית ישירה בתאריך c, אך שוכח להצהיר עליה בקובץ ה-build a/BUILD.

a / a.in  
        import b;
        import c;
        b.foo();
        c.garply();
      
 
תרשים תלות של הצהרה עם חיצים שמחברים את a, b ו-c
תרשים תלותהצהרת
תרשים תלות בפועל עם חצים שמחברים את a, b ו-c. מעכשיו
 חץ שמחבר בין A ל-C. הערך הזה לא תואם
                  לתרשים התלות המוצהר
ממש תרשים תלות

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

3. הבדלים בין תרשימי תלות מוצהרים לבין תרשימי תלות בפועל

הסכנה נגרמת כאשר גורם חדש גורם b כך שהיא כבר לא תלויה c בשבירה של a בטעות.

  b/BUILD
 
rule(
    name = "b",
    srcs = "b.in",
    deps = "//d:d",
)
      
  b / b.in
 
      import d;
      function foo() {
        d.baz();
      }
      
תרשים תלות של הצהרה עם חיצים שמחברים את ב' ו-ב'.
                  ב' לא מתחבר יותר ל-c, ואז מתנתק החיבור ל-c
תרשים תלותהצהרת
תרשים תלות בפועל שמציג את החיבור ל-b ול-c, אבל לא מתחבר יותר ל-c
ממש תרשים תלות

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

ייתכן שהבעיה בוטלה על ידי וידוא שהתלות בפועל מ-a ל-c שהוצהרה בשלב 2 הוצהר כראוי בקובץ BUILD.

סוגי יחסי תלות

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

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

srcs יחסי תלות

קבצים שנצרכו ישירות על ידי הכלל או הכללים שמפיקים קובצי מקור.

deps יחסי תלות

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

data יחסי תלות

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

מערכת ה-build מבצעת בדיקות בספרייה מבודדת שבה רק קבצים שרשומים בתור data זמינים. לכן, אם צריך להפעיל כמה קבצים בינאריים/ספרייה/בדיקה, צריך לציין אותם (או כלל build שמכיל אותם) ב-data. למשל:

# I need a config file from a directory named env:
java_binary(
    name = "setenv",
    ...
    data = [":env/default_env.txt"],
)

# I need test data from another directory
sh_test(
    name = "regtest",
    srcs = ["regtest.sh"],
    data = [
        "//data:file1.txt",
        "//data:file2.txt",
        ...
    ],
)

הקבצים האלה זמינים בנתיב היחסי path/to/data/file. בבדיקות, ניתן לעיין בקבצים האלה על ידי הצטרפות לנתיבים שבספריית המקור של הבדיקה ולנתיב היחסי, למשל ${TEST_SRCDIR}/workspace/path/to/data/file.

שימוש בתוויות להפניה לספריות

כאשר תעיינו בקובצי BUILD שלנו, ייתכן שתבחינו בכך שחלק מהתוויות של data מתייחסות לספריות. התוויות האלה מסתיימות ב-/. או ב-/ כמו בדוגמאות האלה, אסור להשתמש בהן:

לא מומלץdata = ["//data/regression:unittest/."]

לא מומלץdata = ["testdata/."]

לא מומלץdata = ["testdata/"]

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

אבל אל תעשו את זה. כדי להבטיח בנייה מחדש מצטברת נכונה (והפעלה מחדש של בדיקות) לאחר שינוי, מערכת ה-build צריכה להיות מודעת לקבוצה המלאה של הקבצים שמזינים בסביבת ה-build (או לבדיקה). כשאתם מציינים ספרייה, מערכת ה-build מבצעת בנייה רק כשהספרייה עצמה משתנה (בגלל הוספה או מחיקה של קבצים), אבל היא לא יכולה לזהות עריכה של קבצים בודדים כי השינויים האלה לא משפיעים על ספריית הסוגריים. במקום לציין ספריות כקלט במערכת build, יש לציין את קבוצת הקבצים הכלולים בהן במפורש או באמצעות הפונקציה glob(). (יש להשתמש ב-** כדי לאלץ את ה-glob() להיות רקורסיבי.)

מומלץdata = glob(["testdata/**"])

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

אם אתם צריכים להשתמש בתוויות ספרייה, חשוב לזכור שלא ניתן להתייחס לחבילה ההורה עם נתיב יחסי של ../. במקום זאת, השתמשו בנתיב מוחלט כמו //data/regression:unittest/..

כל כלל חיצוני, כמו בדיקה, שצריך להשתמש בכמה קבצים, צריך להצהיר במפורש על התלות שלו בכולם. אפשר להשתמש ב-filegroup() כדי לקבץ קבצים בקובץ BUILD:

filegroup(
        name = 'my_data',
        srcs = glob(['my_unittest_data/*'])
)

לאחר מכן אפשר להפנות את התווית my_data לתלות הנתונים בבדיקה.

קובצי BUILD הרשאות גישה