הגדרות אישיות

דף זה מכסה את היתרונות ואת השימוש הבסיסי בהגדרות של Starlark, Bazel's API להתאמה אישית של בניית הפרויקט. הוא כולל איך לקבוע את הגדרות Build ומספק דוגמאות.

זה מאפשר:

  • הגדרת סימונים מותאמים אישית לפרויקט, כך שלא יהיה צורך ב--define
  • כתוב מעברים כדי להגדיר את נקודות המבט בתצורות השונות מאלה של ההורים (למשל --compilation_mode=opt או --cpu=arm)
  • החלת ברירות מחדל טובות יותר על כללים (למשל, יצירה אוטומטית של //my:android_app באמצעות SDK ספציפי)

ועוד, והכול מתוך קובצי bzl (אין צורך במהדורת Bazel). להצגת bazelbuild/examplesהדוגמאות .

הגדרות build בהגדרת המשתמש

הגדרת build היא קטע יחיד של הגדרות. אפשר לחשוב על הגדרה בתור מפת מפתח/ערך. ההגדרות --cpu=ppc ו---copt="-DFoo" יוצרות הגדרה שנראית {cpu: ppc, copt: "-DFoo"}. כל רשומה היא הגדרת build.

סימונים מסורתיים כמו cpu ו-copt הם הגדרות מקוריות – המפתחות שלהם מוגדרים והערכים שלהם מוגדרים בתוך קוד JavaScript מקורי של bzel. משתמשי Bazel יכולים לקרוא ולכתוב אותם רק דרך שורת הפקודה וממשקי API אחרים שמתחזקים באופן מקורי. כדי לשנות דגלים מקומיים ואת ממשקי ה-API שחושפים אותם, יש צורך בקוד בסיס. הגדרות ה-build שמוגדרות על ידי המשתמשים מוגדרות ב-.bzl קבצים (ולכן אין צורך לשחרר את הבסיס כדי לרשום שינויים). ניתן גם להגדיר אותם דרך שורת הפקודה (אם הם מוגדרים כ-flags, ראו מידע נוסף בהמשך), אך ניתן להגדיר אותם גם באמצעות מעברים שמוגדרים על ידי המשתמש.

קביעת הגדרות build

דוגמה מקצה לקצה

הפרמטר build_setting rule()

הגדרות build הן כללים כמו כל כלל אחר, והן מובדלות באמצעות הפונקציה Starlark rule()'s build_setting .

# example/buildsettings/build_settings.bzl
string_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)

המאפיין build_setting מקבל פונקציה שקובעת את סוג הגדרת ה-build. סוג זה מוגבל לקבוצה של סוגים בסיסיים של Starlark, כגון bool ו-string. פרטים נוספים זמינים בconfigמודול התיעוד. ניתן לבצע הקלדה מורכבת יותר בפונקציית ההטמעה של הכלל. מידע נוסף בנושא מפורט בהמשך.

הפונקציות של המודול config'S כוללות פרמטר בוליאני אופציונלי, flag, שמוגדר כברירת מחדל כ-False. אם flag מוגדר כ-true, ניתן לקבוע את הגדרת ה-build בשורת הפקודה על ידי משתמשים וכן באופן פנימי על ידי כותבי כללים באמצעות ערכי ברירת מחדל ומעברים. לא כל ההגדרות צריכות להיות מוגדרות על ידי משתמשים. לדוגמה, אם ככותב של כלל יש לך מצב ניפוי באגים ואתם רוצים להפעיל אותו במסגרת כללי הבדיקה, אתם לא רוצים לתת למשתמשים את היכולת להפעיל את התכונה ללא כללים נפרדים.

שימוש ב-ctx.build_setting_value

כמו בכל הכללים, גם לכללים ליצירת הגדרות יש פונקציות הטמעה. ניתן לגשת לערך הבסיסי של סוג Starlark של הגדרות build בשיטה ctx.build_setting_value. השיטה הזו זמינה רק ל ctx אובייקטים של כללי הגדרה של build. שיטות ההטמעה האלה יכולות להעביר את הערך של הגדרות ה-build ישירות או לבצע בו פעולות נוספות כמו בדיקת סוג או יצירת מבנה מורכב יותר. כך תטמיעו הגדרת build בהקלדה של enum:

# example/buildsettings/build_settings.bzl
TemperatureProvider = provider(fields = ['type'])

temperatures = ["HOT", "LUKEWARM", "ICED"]

def _impl(ctx):
    raw_temperature = ctx.build_setting_value
    if raw_temperature not in temperatures:
        fail(str(ctx.label) + " build setting allowed to take values {"
             + ", ".join(temperatures) + "} but was set to unallowed value "
             + raw_temperature)
    return TemperatureProvider(type = raw_temperature)

temperature = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)

הגדרת התרעות לגבי ריבוי מחרוזות

בהגדרות המחרוזת יש פרמטר נוסף של allow_multiple, שמאפשר להגדיר את הסימון מספר פעמים בשורת הפקודה או ב-bazelrcs. ערך ברירת המחדל שלהם עדיין מוגדר באמצעות מאפיין מסוג מחרוזת:

# example/buildsettings/build_settings.bzl
allow_multiple_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True, allow_multiple = True)
)
# example/buildsettings/BUILD
load("//example/buildsettings:build_settings.bzl", "allow_multiple_flag")
allow_multiple_flag(
    name = "roasts",
    build_setting_default = "medium"
)

כל הגדרה של הדגל נחשבת כערך יחיד:

$ bazel build //my/target --//example:roasts=blonde \
    --//example:roasts=medium,dark

הנתונים שלמעלה ינותחו אל {"//example:roasts": ["blonde", "medium,dark"]} ו ctx.build_setting_value תחזיר את הרשימה ["blonde", "medium,dark"].

יצירת הגדרות build מידיות

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

# example/buildsettings/build_settings.bzl
FlavorProvider = provider(fields = ['type'])

def _impl(ctx):
    return FlavorProvider(type = ctx.build_setting_value)

flavor = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)
# example/buildsettings/BUILD
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
    name = "favorite_flavor",
    build_setting_default = "APPLE"
)

הגדרות מוגדרות מראש

דוגמה מקצה לקצה

הספרייה Skylib כוללת קבוצה של הגדרות מוגדרות מראש שניתן ליצור ללא צורך בכתיבה כדי להשתמש בתכונה Starlark בהתאמה אישית.

לדוגמה, כדי לקבוע הגדרה שמקבלת קבוצה מוגבלת של ערכי מחרוזת:

# example/BUILD
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
string_flag(
    name = "myflag",
    values = ["a", "b", "c"],
    build_setting_default = "a",
)

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

שימוש בהגדרות build

בהתאם להגדרות ה-build

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

# example/rules.bzl
load("//example/buildsettings:build_settings.bzl", "FlavorProvider")
def _rule_impl(ctx):
    if ctx.attr.flavor[FlavorProvider].type == "ORANGE":
        ...

drink_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "flavor": attr.label()
    }
)
# example/BUILD
load("//example:rules.bzl", "drink_rule")
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
    name = "favorite_flavor",
    build_setting_default = "APPLE"
)
drink_rule(
    name = "my_drink",
    flavor = ":favorite_flavor",
)

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

# kotlin/rules.bzl
_KOTLIN_CONFIG = {
    "_compiler": attr.label(default = "//kotlin/config:compiler-flag"),
    "_mode": attr.label(default = "//kotlin/config:mode-flag"),
    ...
}

...

kotlin_library = rule(
    implementation = _rule_impl,
    attrs = dicts.add({
        "library-attr": attr.string()
    }, _KOTLIN_CONFIG)
)

kotlin_binary = rule(
    implementation = _binary_impl,
    attrs = dicts.add({
        "binary-attr": attr.label()
    }, _KOTLIN_CONFIG)

שימוש בהגדרות של build בשורת הפקודה

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

$ bazel build //my/target --//example:string_flag=some-value # allowed
$ bazel build //my/target --//example:string_flag some-value # not allowed

יש תמיכה בתחביר בוליאני מיוחד:

$ bazel build //my/target --//example:boolean_flag
$ bazel build //my/target --no//example:boolean_flag

שימוש בכינויים של הגדרות build

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

מגדירים כינוי על ידי הוספת --flag_alias=ALIAS_NAME=TARGET_PATH ל-.bazelrc . לדוגמה, כדי להגדיר כינוי כ-coffee:

# .bazelrc
build --flag_alias=coffee=//experimental/user/starlark_configurations/basic_build_setting:coffee-temp

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

כדי להשתמש בכינוי, יש להקליד אותו במקום את נתיב היעד של הגדרת ה-build. בדוגמה שלמעלה של coffee שהוגדר ב.bazelrc של המשתמש:

$ bazel build //my/target --coffee=ICED

במקום

$ bazel build //my/target --//experimental/user/starlark_configurations/basic_build_setting:coffee-temp=ICED

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

הגדרות של build לפי תווית

דוגמה מקצה לקצה

בניגוד להגדרות build אחרות, לא ניתן להגדיר הגדרות שמבוססות על תוויות באמצעות הפרמטר build_setting של הכלל. במקום זאת, לבוז יש שני כללים מובנים: label_flag וlabel_setting. כללים אלה מעבירים את הספקים של היעד שאליו מוגדרת הגדרת ה-build בפועל. label_flag וכן label_setting ניתן לקרוא/לכתוב מעברים, והמשתמש יכול להגדיר label_flag את הכללים של build_setting אחרים. ההבדל היחיד ביניהם הוא שאין אפשרות להגדיר אותם בהתאמה אישית.

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

# example/rules.bzl
MyProvider = provider(fields = ["my_field"])

def _dep_impl(ctx):
    return MyProvider(my_field = "yeehaw")

dep_rule = rule(
    implementation = _dep_impl
)

def _parent_impl(ctx):
    if ctx.attr.my_field_provider[MyProvider].my_field == "cowabunga":
        ...

parent_rule = rule(
    implementation = _parent_impl,
    attrs = { "my_field_provider": attr.label() }
)

# example/BUILD
load("//example:rules.bzl", "dep_rule", "parent_rule")

dep_rule(name = "dep")

parent_rule(name = "parent", my_field_provider = ":my_field_provider")

label_flag(
    name = "my_field_provider",
    build_setting_default = ":dep"
)

Build settings ו-select()

דוגמה מקצה לקצה

המשתמשים יכולים לקבוע מאפיינים בהגדרות build באמצעות select(). ניתן להעביר יעדים של הגדרת build אל המאפיין flag_values של config_setting. הערך שיש להתאים לתצורה מועבר כ-String, ולאחר מכן מנתחים אותו לסוג ההגדרה של build לצורך התאמה.

config_setting(
    name = "my_config",
    flag_values = {
        "//example:favorite_flavor": "MANGO"
    }
)

מעברים שמוגדרים על ידי המשתמש

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

כללים שמגדירים אותם חייבים לכלול מאפיין מיוחד:

  "_allowlist_function_transition": attr.label(
      default = "@bazel_tools//tools/allowlists/function_transition_allowlist"
  )

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

הגדרה

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

באופן רשמי, מעבר הוא פונקציה מהגדרת קלט לתצורה אחת או יותר של פלט. רוב המעברים הם ביחס גובה-רוחב של 1:1, כמו "החלפת ההגדרה האישית של הקלט ב---cpu=ppc". כמו כן, יכולים להתקיים מעברים ביחס של 1:2, אבל יש להם מגבלות מיוחדות.

ב-Starlark, מעברים מוגדרים בצורה דומה לכללים, עם פונקציית transition() הגדרה ופונקציית הטמעה.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {"//example:favorite_flavor" : "MINT"}

hot_chocolate_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

הפונקציה transition() מקבלת פונקציית הטמעה, קבוצה של הגדרות build לקריאה (inputs) וקבוצה של הגדרות build לצורך כתיבה (outputs). פונקציית ההטמעה כוללת שני פרמטרים, settings ו-attr. settings הוא מילון {String:Object} מכל ההגדרות שצוינו בפרמטר inputs אל transition().

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

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

הגדרת 1:2 מעברים

דוגמה מקצה לקצה

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

מעברי 1:2+ מוגדרים על ידי החזרה של רשימת מילונים בפונקציית ההטמעה של המעבר.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return [
        {"//example:favorite_flavor" : "LATTE"},
        {"//example:favorite_flavor" : "MOCHA"},
    ]

coffee_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

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

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {
        "Apple deps": {"//command_line_option:cpu": "ppc"},
        "Linux deps": {"//command_line_option:cpu": "x86"},
    }

multi_arch_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]
)

צירוף מעברים

דוגמה מקצה לקצה

ניתן לצרף את המעברים בשני מקומות: קצוות נכנסים וקצוות יוצאים. בפועל, המשמעות היא שכללים יכולים להעביר את התצורה שלהם (מעבר נכנס לקצה) ולהעביר את התלות שלהם' תצורות (מעבר קצה יוצא).

הערה: בשלב זה אין אפשרות לצרף מעברים של Starlark לכללים מקומיים. אם צריך לעשות זאת, יש לפנות bazel-דיון@googlegroups.com לעזרה בפתרון בעיות.

מעברי קצה נכנסים

מעברי קצה נכנסים מופעלים על ידי צירוף אובייקט transition (נוצר על ידי transition()) לפרמטר rule()'cfg:

# example/rules.bzl
load("example/transitions:transitions.bzl", "hot_chocolate_transition")
drink_rule = rule(
    implementation = _impl,
    cfg = hot_chocolate_transition,
    ...

מעברי קצה נכנסים חייבים להיות מעברים ביחס גובה-רוחב של 1:1.

מעברי קצה יוצאים

מעברים קצוות יוצאים מופעלים על ידי צירוף אובייקט transition (נוצר על ידי transition()) לפרמטר cfg של המאפיין:

# example/rules.bzl
load("example/transitions:transitions.bzl", "coffee_transition")
drink_rule = rule(
    implementation = _impl,
    attrs = { "dep": attr.label(cfg = coffee_transition)}
    ...

מעברי קצה יוצאים יכולים להיות 1:1 או 1:2+.

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

מעברים בין אפשרויות מותאמות

דוגמה מקצה לקצה

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

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {"//command_line_option:cpu": "k8"}

cpu_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]

אפשרויות מותאמות שאינן נתמכות

Bazel אינה תומכת במעבר ב---define עם "//command_line_option:define". במקום זאת, צריך להשתמש בהגדרת מבנה מותאמת אישית. באופן כללי, לא מומלץ להשתמש בתכונות חדשות של --define לטובת הגדרות build.

Bazel אינה תומכת במעבר ב---config. הסיבה לכך היא שהסימון --config הוא "span" המתרחב לסימונים אחרים.

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

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

אפשרות המעברים מופעלת במספר הגדרות של build

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

# example/buildsettings/build_settings.bzl
string_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True, allow_multiple = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "string_flag")
string_flag(name = "roasts", build_setting_default = "medium")
# example/transitions/rules.bzl
def _transition_impl(settings, attr):
    # Using a value of just "dark" here will throw an error
    return {"//example:roasts" : ["dark"]},

coffee_transition = transition(
    implementation = _transition_impl,
    inputs = [],
    outputs = ["//example:roasts"]
)

מעברים ללא פעילות

אם המעבר מחזיר {}, [] או None, זהו קיצור דרך לשמירת כל ההגדרות בערך המקורי שלהן. הפעולה הזו יכולה להיות נוחה יותר מאשר הגדרה מפורשת של כל פלט בנפרד.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (attr)
    if settings["//example:already_chosen"] is True:
      return {}
    return {
      "//example:favorite_flavor": "dark chocolate",
      "//example:include_marshmallows": "yes",
      "//example:desired_temperature": "38C",
    }

hot_chocolate_transition = transition(
    implementation = _impl,
    inputs = ["//example:already_chosen"],
    outputs = [
        "//example:favorite_flavor",
        "//example:include_marshmallows",
        "//example:desired_temperature",
    ]
)

גישה למאפיינים באמצעות מעברים

דוגמה מקצה לקצה

כשמחברים מעבר לקצה יוצא (גם אם המעבר הוא יחס של 1:1 או 1:2 ומעלה), ctx.attr מאלצת להיות רשימה אם היא עדיין לא מוגדרת. סדר הרכיבים ברשימה הזו לא צוין.

# example/transitions/rules.bzl
def _transition_impl(settings, attr):
    return {"//example:favorite_flavor" : "LATTE"},

coffee_transition = transition(
    implementation = _transition_impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

def _rule_impl(ctx):
    # Note: List access even though "dep" is not declared as list
    transitioned_dep = ctx.attr.dep[0]

    # Note: Access doesn't change, other_deps was already a list
    for other dep in ctx.attr.other_deps:
      # ...


coffee_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "dep": attr.label(cfg = coffee_transition)
        "other_deps": attr.label_list(cfg = coffee_transition)
    })

אם המעבר הוא 1:2+ ומגדיר מפתחות מותאמים אישית, ניתן להשתמש ב-ctx.split_attr כדי לקרוא נקודות מגע בודדות עם כל מפתח:

# example/transitions/rules.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {
        "Apple deps": {"//command_line_option:cpu": "ppc"},
        "Linux deps": {"//command_line_option:cpu": "x86"},
    }

multi_arch_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]
)

def _rule_impl(ctx):
    apple_dep = ctx.split_attr.dep["Apple deps"]
    linux_dep = ctx.split_attr.dep["Linux deps"]
    # ctx.attr has a list of all deps for all keys. Order is not guaranteed.
    all_deps = ctx.attr.dep

multi_arch_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "dep": attr.label(cfg = multi_arch_transition)
    })

כאן תוכלו לראות דוגמה מלאה.

שילוב עם פלטפורמות וארגזי כלים

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

שיקולי זיכרון וביצועים

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

גרסאות build שהתנהגו באופן שגוי: מקרה לדוגמה

תרשים מדרגיות

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

התרשים הזה מציג יעד ברמה עליונה, //pkg:app, שתלוי בשני יעדים, a////pkg:1_0 ו-//pkg:1_1. שני היעדים האלה תלויים בשני יעדים: //pkg:2_0 ו- //pkg:2_1. שני היעדים האלה תלויים בשני יעדים: //pkg:3_0 ו-//pkg:3_1. הפעולה נמשכת עד //pkg:n_0 ו-//pkg:n_1, ששניהם תלויים ביעד יחיד, //pkg:dep.

מבנה //pkg:app דורש \(2n+2\) יעדים:

  • //pkg:app
  • //pkg:dep
  • //pkg:i_0 ו//pkg:i_1 עבור \(i\) ב- \([1..n]\)

נניח שאתם מיישמים) סימון --//foo:owner=<STRING> וחלים //pkg:i_b

depConfig = myConfig + depConfig.owner="$(myConfig.owner)$(b)"

במילים אחרות, //pkg:i_b מצרף b את הערך הישן של --owner בכל נקודות הפנייה.

פעולה זו יוצרת את היעדים המוגדרים הבאים:

//pkg:app                              //foo:owner=""
//pkg:1_0                              //foo:owner=""
//pkg:1_1                              //foo:owner=""
//pkg:2_0 (via //pkg:1_0)              //foo:owner="0"
//pkg:2_0 (via //pkg:1_1)              //foo:owner="1"
//pkg:2_1 (via //pkg:1_0)              //foo:owner="0"
//pkg:2_1 (via //pkg:1_1)              //foo:owner="1"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_0)  //foo:owner="00"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_1)  //foo:owner="01"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_0)  //foo:owner="10"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_1)  //foo:owner="11"
...

//pkg:dep מפיקה את היעדים \(2^n\) המוגדרים: config.owner= "\(b_0b_1...b_n\)" לכולם \(b_i\) ב- \(\{0,1\}\).

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

להתחלת התהליך: מוסיפים שיטות למדידה ולצמצום הבעיות.

קריאה נוספת

פרטים נוספים על שינוי הגדרות build זמינים בכתובת: