מאפיינים הניתנים להגדרה, הידועים בשם select()
, הם תכונה של Bazel שמאפשרת למשתמשים להחליף את
הערכים של מאפייני כלל build בשורת הפקודה.
ניתן להשתמש בכך, לדוגמה, עבור ספרייה עם פלטפורמות שונות שבוחרים באופן אוטומטי את היישום המתאים לארכיטקטורה, או עבור קובץ בינארי המאפשר הגדרה מותאמת אישית בעת ההתאמה.
דוגמה
# myapp/BUILD
cc_binary(
name = "mybinary",
srcs = ["main.cc"],
deps = select({
":arm_build": [":arm_lib"],
":x86_debug_build": [":x86_dev_lib"],
"//conditions:default": [":generic_lib"],
}),
)
config_setting(
name = "arm_build",
values = {"cpu": "arm"},
)
config_setting(
name = "x86_debug_build",
values = {
"cpu": "x86",
"compilation_mode": "dbg",
},
)
זה מצהיר על cc_binary
ש &מירכאות;&chooses באופן ספציפי, deps
הופך ל:
Command | נקודות נקודות = |
bazel build //myapp:mybinary --cpu=arm |
[":arm_lib"] |
bazel build //myapp:mybinary -c dbg --cpu=x86 |
[":x86_dev_lib"] |
bazel build //myapp:mybinary --cpu=ppc |
[":generic_lib"] |
bazel build //myapp:mybinary -c dbg --cpu=ppc |
[":generic_lib"] |
select()
משמש כ-placeholder לערך שייבחר על סמך
תנאי תצורה, שהם תוויות שמתייחסות ליעדים של config_setting
. על ידי שימוש ב-select()
במאפיין שניתן להגדרה, המאפיין
מקבל בפועל ערכים שונים כאשר מתקיימים תנאים שונים.
ההתאמות צריכות להיות חד-משמעיות: צריך להתקיים תנאי אחד בדיוק. אם
מספר תנאים תואמים, התנאי values
צריך להיות קבוצת-על של כל התנאים האחרים. לדוגמה, values = {"cpu": "x86", "compilation_mode": "dbg"}
היא התמחות חד-משמעית של values = {"cpu": "x86"}
. התנאי המובנה
//conditions:default
תואם באופן אוטומטי
אם אין שום דבר אחר.
בדוגמה הזו נעשה שימוש ב-deps
, אבל select()
פועל היטב גם ב-srcs
,
resources
, ב-cmd
וברוב המאפיינים האחרים. רק מספר קטן של מאפיינים
לא ניתנים להגדרה, ויש בהם הערות ברורות. לדוגמה,
המאפיין config_setting
'עצמו
values
לא ניתן להגדרה.
select()
ותלויים
מאפיינים מסוימים משנים את הפרמטרים של ה-build לכל התלויות העקיפות
במסגרת יעד. לדוגמה, הפונקציה tools
&genrule
משנה את --cpu
ליחידת העיבוד המרכזית (CPU)
של Bazel (שאינה עשויה להיות מורכבת מיחידת עיבוד מרכזית (CPU), ייתכן שהיא שונה
מיחידת העיבוד המרכזית (CPU) שהיעד מיועד אליה. פעולה זו נקראת
מעבר להגדרות אישיות.
נתון
#myapp/BUILD
config_setting(
name = "arm_cpu",
values = {"cpu": "arm"},
)
config_setting(
name = "x86_cpu",
values = {"cpu": "x86"},
)
genrule(
name = "my_genrule",
srcs = select({
":arm_cpu": ["g_arm.src"],
":x86_cpu": ["g_x86.src"],
}),
tools = select({
":arm_cpu": [":tool1"],
":x86_cpu": [":tool2"],
}),
)
cc_binary(
name = "tool1",
srcs = select({
":arm_cpu": ["armtool.cc"],
":x86_cpu": ["x86tool.cc"],
}),
)
ריצה
$ bazel build //myapp:my_genrule --cpu=arm
במכונה למפתחים של x86
, מתבצע חיבור בין build ל-g_arm.src
, tool1
ו-x86tool.cc
. שני הפרמטרים (select
) המצורפים אל my_genrule
משתמשים בפרמטרים של my_genrule
' הכוללים את --cpu=arm
. המאפיין tools
משתנה ל---cpu
ל-x86
בהתאם לtool1
ולתלויות העקיפות שלו. select
ב-tool1
משתמש בפרמטרים של tool1
&build, כולל --cpu=x86
.
תנאים בקשר להגדרות
כל מפתח במאפיין שניתן להגדרה הוא הפניה לתווית config_setting
או
constraint_value
.
config_setting
הוא רק אוסף של
הגדרות ניסיוניות צפויות בשורת הפקודה. הכללה של היעדים האלה בטירגוט אחד מקלה על התחזוקה של תנאים "standard" שמשתמשים יכולים להפנות ממספר מקומות.
constraint_value
מספקת תמיכה בהתנהגות של פלטפורמות מרובות.
סימונים מובנים
סימונים כמו --cpu
מובנים ב-Bazel: כלי ה-build עוזר להבין
את כל ה-build בכל הפרויקטים. הערכים האלה מסומנים במאפיין
config_setting
's
values
:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN
הוא שם דגל (ללא --
, כך ש-"cpu"
במקום "--cpu"
). valueN
הוא הערך הצפוי של הסימון הזה. :meaningful_condition_name
תואם אם
כל הרשומה ב-values
תואמת. ההזמנה לא רלוונטית.
הניתוח של valueN
מתבצע כאילו הוא הוגדר בשורת הפקודה. מה זה אומר?
values = { "compilation_mode": "opt" }
התאמותbazel build -c opt
values = { "force_pic": "true" }
התאמותbazel build --force_pic=1
values = { "force_pic": "0" }
התאמותbazel build --noforce_pic
המדיניות config_setting
תומכת רק בסימונים שמשפיעים על התנהגות היעד. לדוגמה, אסור
--show_progress
'
להשפיע רק על האופן שבו דוחות Bazel מתקדמים למשתמש. טירגוטים לא יכולים להשתמש
בסימון הזה כדי ליצור את התוצאות. הקבוצה המדויקת של הסימונים הנתמכים לא מתועדת. ניתן להשתמש ברוב הסימונים
סימונים מותאמים אישית
אתם יכולים ליצור מודלים משלכם לפרויקטים ספציפיים באמצעות הגדרות build של Starlark. בניגוד לסימונים מובנים, הם מוגדרים כיעדי build, כך ש-Bazel מפנה אליהם תוויות תוויות.
ההתראות מופעלות באמצעות המאפיין config_setting
'
flag_values
:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
ההתנהגות זהה לסימונים מובנים. כאן אפשר לראות דוגמה לעבודה.
--define
הוא תחביר מדור קודם חלופי עבור סימונים מותאמים אישית (לדוגמה
--define foo=bar
). אפשר לבטא זאת במאפיין
values
(values = {"define": "foo=bar"}
) או במאפיין
configure_value
(define_values = {"foo": "bar"}
). אפשר להשתמש ב---define
רק לצורך תאימות. עדיפות להגדרות של BuildStark ככל האפשר.
values
, flag_values
וdefine_values
מבצעים הערכה בנפרד. המאפיין
config_setting
תואם אם כל הערכים בכולם תואמים.
תנאי ברירת המחדל
התנאי המובנה //conditions:default
תואם כאשר אין תנאי אחר תואם.
בגלל הכלל "בהתאמה אחת בלבד" מאפיין שאפשר להגדיר בלי התאמה
וללא תנאי ברירת מחדל גורם לשגיאה "no matching conditions"
. התכונה הזו יכולה
להגן מפני כשלים שקטים בהגדרות בלתי צפויות:
# myapp/BUILD
config_setting(
name = "x86_cpu",
values = {"cpu": "x86"},
)
cc_library(
name = "x86_only_lib",
srcs = select({
":x86_cpu": ["lib.cc"],
}),
)
$ bazel build //myapp:x86_only_lib --cpu=arm
ERROR: Configurable attribute "srcs" doesn't match this configuration (would
a default condition help?).
Conditions checked:
//myapp:x86_cpu
כדי לקבל שגיאות ברורות עוד יותר, ניתן להגדיר הודעות מותאמות אישית באמצעות המאפיין select()
'
no_match_error
.
פלטפורמות
אמנם אפשר לציין מספר סימונים בשורת הפקודה כדי לשמור על גמישות, אבל אפשר גם להעמיס על כל אחד מהסימונים בכל פעם שרוצים ליצור יעד. פלטפורמות מאפשרות לאחד אותן לחבילות פשוטות.
# myapp/BUILD
sh_binary(
name = "my_rocks",
srcs = select({
":basalt": ["pyroxene.sh"],
":marble": ["calcite.sh"],
"//conditions:default": ["feldspar.sh"],
}),
)
config_setting(
name = "basalt",
constraint_values = [
":black",
":igneous",
],
)
config_setting(
name = "marble",
constraint_values = [
":white",
":metamorphic",
],
)
# constraint_setting acts as an enum type, and constraint_value as an enum value.
constraint_setting(name = "color")
constraint_value(name = "black", constraint_setting = "color")
constraint_value(name = "white", constraint_setting = "color")
constraint_setting(name = "texture")
constraint_value(name = "smooth", constraint_setting = "texture")
constraint_setting(name = "type")
constraint_value(name = "igneous", constraint_setting = "type")
constraint_value(name = "metamorphic", constraint_setting = "type")
platform(
name = "basalt_platform",
constraint_values = [
":black",
":igneous",
],
)
platform(
name = "marble_platform",
constraint_values = [
":white",
":smooth",
":metamorphic",
],
)
ניתן לציין את הפלטפורמה בשורת הפקודה. הוא מפעיל את config_setting
שמכילים קבוצת משנה של הפלטפורמה constraint_values
,
כדי לאפשר ל-config_setting
להתאים בין ביטויים ב-select()
.
לדוגמה, כדי להגדיר את המאפיין srcs
כ-my_rocks
ל-calcite.sh
,
אפשר פשוט להריץ
bazel build //my_app:my_rocks --platforms=//myapp:marble_platform
ללא פלטפורמות, הקטע עשוי להיראות בערך כך
bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic
select()
יכול גם לקרוא ישירות את constraint_value
:
constraint_setting(name = "type")
constraint_value(name = "igneous", constraint_setting = "type")
constraint_value(name = "metamorphic", constraint_setting = "type")
sh_binary(
name = "my_rocks",
srcs = select({
":igneous": ["igneous.sh"],
":metamorphic" ["metamorphic.sh"],
}),
)
פעולה זו חוסכת לך את הצורך להתקין מכשירים רק config_setting
כשצריך לבדוק רק ערכים בודדים.
הפלטפורמות עדיין בשלבי פיתוח. פרטים נוספים זמינים בתיעוד.
שילוב של select()
שניות
המאפיין select
יכול להופיע כמה פעמים באותו מאפיין:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"] +
select({
":armeabi_mode": ["armeabi_src.sh"],
":x86_mode": ["x86_src.sh"],
}) +
select({
":opt_mode": ["opt_extras.sh"],
":dbg_mode": ["dbg_extras.sh"],
}),
)
select
לא יכול להופיע בתוך select
אחר. אם צריך לשייך את selects
והמאפיין מקבל יעדים אחרים כערכים, יש להשתמש ביעד ביניים:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":armeabi_mode": [":armeabi_lib"],
...
}),
)
sh_library(
name = "armeabi_lib",
srcs = select({
":opt_mode": ["armeabi_with_opt.sh"],
...
}),
)
אם יש צורך ב-select
כדי להתאים כאשר יש מספר תנאים, מומלץ וגם
לשרשראות.
או שרשור
כמה נקודות שכדאי לזכור:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1": [":standard_lib"],
":config2": [":standard_lib"],
":config3": [":standard_lib"],
":config4": [":special_lib"],
}),
)
רוב התנאים מעריכים את אותו פילוח, אבל קשה לקרוא את הטקסט בעזרת תחביר. מומלץ שלא לחזור על [":standard_lib"]
מספר פעמים.
אפשרות אחת היא להגדיר מראש את הערך כמשתנה BUILD:
STANDARD_DEP = [":standard_lib"]
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1": STANDARD_DEP,
":config2": STANDARD_DEP,
":config3": STANDARD_DEP,
":config4": [":special_lib"],
}),
)
כך קל יותר לנהל את התלות. אבל זה עדיין גורם לשכפול לא נחוץ.
לקבלת תמיכה ישירה יותר, ניתן להשתמש באחת מהאפשרויות הבאות:
selects.with_or
המאקרו
with_or
במודול Skylib'
selects
תומך בתנאי OR
ישירות בתוך select
:
load("@bazel_skylib//lib:selects.bzl", "selects")
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = selects.with_or({
(":config1", ":config2", ":config3"): [":standard_lib"],
":config4": [":special_lib"],
}),
)
selects.config_setting_group
המאקרו
config_setting_group
במערכת Skylib's
selects
תומך OR
במספר OR
:
load("@bazel_skylib//lib:selects.bzl", "selects")
config_setting(
name = "config1",
values = {"cpu": "arm"},
)
config_setting(
name = "config2",
values = {"compilation_mode": "dbg"},
)
selects.config_setting_group(
name = "config1_or_2",
match_any = [":config1", ":config2"],
)
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1_or_2": [":standard_lib"],
"//conditions:default": [":other_lib"],
}),
)
בניגוד ל-selects.with_or
, יעדים שונים יכולים לשתף את :config1_or_2
במאפיינים שונים.
יש שגיאה במספר תנאים כדי להתאים להם, אלא אם אחד מהם מוגדר כתנאי חד-משמעי "ההתמחות. עיינו כאן לקבלת פרטים נוספים.
ושרשור
אם יש לך סניף של select
לצורך התאמה כאשר יש התאמה של מספר תנאים, יש להשתמש במאקרו
Skylib
config_setting_group:
config_setting(
name = "config1",
values = {"cpu": "arm"},
)
config_setting(
name = "config2",
values = {"compilation_mode": "dbg"},
)
selects.config_setting_group(
name = "config1_and_2",
match_all = [":config1", ":config2"],
)
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1_and_2": [":standard_lib"],
"//conditions:default": [":other_lib"],
}),
)
בניגוד ל-OR, לא ניתן לקבוע config_setting
קיימים.
AND
בתוך select
. צריך לעטוף אותם באופן מפורש באמצעות config_setting_group
.
הודעות שגיאה בהתאמה אישית
כברירת מחדל, כשאין תנאי תואם, היעד select()
מצורף
לכשלים עם השגיאה:
ERROR: Configurable attribute "deps" doesn't match this configuration (would
a default condition help?).
Conditions checked:
//tools/cc_target_os:darwin
//tools/cc_target_os:android
אפשר להתאים אישית את המאפיין הזה באמצעות המאפיין no_match_error
:
cc_library(
name = "my_lib",
deps = select(
{
"//tools/cc_target_os:android": [":android_deps"],
"//tools/cc_target_os:windows": [":windows_deps"],
},
no_match_error = "Please build with an Android or Windows toolchain",
),
)
$ bazel build //myapp:my_lib
ERROR: Configurable attribute "deps" doesn't match this configuration: Please
build with an Android or Windows toolchain
תאימות לכללים
הטמעות של כללים מקבלות ערכים קבועים של מאפיינים שניתן להגדיר. לדוגמה, בהינתן:
# myapp/BUILD
some_rule(
name = "my_target",
some_attr = select({
":foo_mode": [":foo"],
":bar_mode": [":bar"],
}),
)
$ bazel build //myapp/my_target --define mode=foo
קוד ההטמעה של הכלל רואה את ctx.attr.some_attr
כ-[":foo"]
.
רכיבי מאקרו יכולים לקבל select()
סעיפים ולהעביר אותם לכללים
מקומיים. עם זאת, הם לא יכולים לשנות אותן ישירות. לדוגמה, אין דרך
לאפשר המרה של מאקרו
select({"foo": "val"}, ...)
אל
select({"foo": "val_with_suffix"}, ...)
יש לכך שתי סיבות.
תחילה, פקודות מאקרו שצריכות לדעת באיזו נתיב select
בוחר לא יכולות לעבוד, מכיוון שפונקציות מאקרו מוערכים בשלב הטעינה של Bazel
שמתרחש לפני שערכי הדגל ידועים.
זוהי מגבלת עיצוב של Bazel שלא צפויה להשתנות בקרוב.
שנית, רכיבי מאקרו שפשוט צריכים לחזור על עצמם בכל select
הנתיבים, אם זה אפשרי מבחינה טכנית, אינם כוללים ממשק משתמש עקבי. יש צורך בעיצוב נוסף כדי לשנות זאת.
שאילתה ושאילתה
Bazel query
פועל מעל Bazel&33;
בטעינה.
פירוש הדבר הוא שהוא לא יודע באיזו שורת פקודה מתבצע סימון של יעד, מכיוון שהסימונים האלה לא נבדקים עד למועד מאוחר יותר (בשלב הניתוח).
כדי שניתן יהיה לקבוע אילו סניפים של select()
ייבחרו.
Bazel cquery
פועל לאחר שלב הניתוח של Bazel&33, ולכן יש לו
את כל המידע הזה והוא יכול לפתור באופן מדויק את select()
.
שקול:
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
# myapp/BUILD
string_flag(
name = "dog_type",
build_setting_default = "cat"
)
cc_library(
name = "my_lib",
deps = select({
":long": [":foo_dep"],
":short": [":bar_dep"],
}),
)
config_setting(
name = "long",
flag_values = {":dog_type": "dachshund"},
)
config_setting(
name = "short",
flag_values = {":dog_type": "pug"},
)
query
אומדנים משוערים של :my_lib
':
$ bazel query 'deps(//myapp:my_lib)'
//myapp:my_lib
//myapp:foo_dep
//myapp:bar_dep
בעוד שהמדד cquery
מציג את תלויותיו המדויקים:
$ bazel cquery 'deps(//myapp:my_lib)' --//myapp:dog_type=pug
//myapp:my_lib
//myapp:bar_dep
שאלות נפוצות
למה אי אפשר להשתמש בסלקטורים (+)?
select() כן פועל בכללים! לפרטים נוספים, אפשר לעיין בתאימות לכללים.
הבעיה העיקרית בדרך כלל היא השאלה ש-select() לא פועל בפקודות מאקרו. הכללים האלה שונים מכללים. כדאי לעיין בתיעוד בנושא כללים ורכיבי מאקרו כדי להבין את ההבדלים. הנה דוגמה מקצה לקצה:
מגדירים כלל ומאקרו:
# myapp/defs.bzl
# Rule implementation: when an attribute is read, all select()s have already
# been resolved. So it looks like a plain old attribute just like any other.
def _impl(ctx):
name = ctx.attr.name
allcaps = ctx.attr.my_config_string.upper() # This works fine on all values.
print("My name is " + name + " with custom message: " + allcaps)
# Rule declaration:
my_custom_bazel_rule = rule(
implementation = _impl,
attrs = {"my_config_string": attr.string()},
)
# Macro declaration:
def my_custom_bazel_macro(name, my_config_string):
allcaps = my_config_string.upper() # This line won't work with select(s).
print("My name is " + name + " with custom message: " + allcaps)
יצירה מיידית של הכלל והמאקרו:
# myapp/BUILD
load("//myapp:defs.bzl", "my_custom_bazel_rule")
load("//myapp:defs.bzl", "my_custom_bazel_macro")
my_custom_bazel_rule(
name = "happy_rule",
my_config_string = select({
"//tools/target_cpu:x86": "first string",
"//tools/target_cpu:ppc": "second string",
}),
)
my_custom_bazel_macro(
name = "happy_macro",
my_config_string = "fixed string",
)
my_custom_bazel_macro(
name = "sad_macro",
my_config_string = select({
"//tools/target_cpu:x86": "first string",
"//tools/target_cpu:ppc": "other string",
}),
)
הבנייה נכשלה כי לא ניתן לעבד את select()
ב-sad_macro
:
$ bazel build //myapp:all
ERROR: /myworkspace/myapp/BUILD:17:1: Traceback
(most recent call last):
File "/myworkspace/myapp/BUILD", line 17
my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
File "/myworkspace/myapp/defs.bzl", line 4, in
my_custom_bazel_macro
my_config_string.upper()
type 'select' has no method upper().
ERROR: error loading package 'myapp': Package 'myapp' contains errors.
התגובה של sad_macro
תטופל בהצלחה:
# Comment out sad_macro so it doesn't mess up the build.
$ bazel build //myapp:all
DEBUG: /myworkspace/myapp/defs.bzl:5:3: My name is happy_macro with custom message: FIXED STRING.
DEBUG: /myworkspace/myapp/hi.bzl:15:3: My name is happy_rule with custom message: FIRST STRING.
לא ניתן לשנות את ההגדרה הזו כי מתבצעת בדיקה של פקודות מאקרו בהגדרה לפני ש-Bazel קוראת את הסימונים של שורת הפקודה של Build. כלומר, אין מספיק מידע כדי להעריך את הערך (#()).
עם זאת, פונקציות מאקרו יכולות להעביר select()
בתור blobs אטומות לכללים:
# myapp/defs.bzl
def my_custom_bazel_macro(name, my_config_string):
print("Invoking macro " + name)
my_custom_bazel_rule(
name = name + "_as_target",
my_config_string = my_config_string,
)
$ bazel build //myapp:sad_macro_less_sad
DEBUG: /myworkspace/myapp/defs.bzl:23:3: Invoking macro sad_macro_less_sad.
DEBUG: /myworkspace/myapp/defs.bzl:15:3: My name is sad_macro_less_sad with custom message: FIRST STRING.
למה select() תמיד מחזירה את הערך true?
מכיוון שפקודות מאקרו (אבל לא כללים) לפי הגדרה
לא ניתן להעריך את select()
s, כל ניסיון לעשות זאת
יוצר בדרך כלל שגיאה:
ERROR: /myworkspace/myapp/BUILD:17:1: Traceback
(most recent call last):
File "/myworkspace/myapp/BUILD", line 17
my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
File "/myworkspace/myapp/defs.bzl", line 4, in
my_custom_bazel_macro
my_config_string.upper()
type 'select' has no method upper().
בוליאניים הם מקרה מיוחד שנכשל בשקט, ולכן עליכם להיות ערניים במיוחד אליהם:
$ cat myapp/defs.bzl
def my_boolean_macro(boolval):
print("TRUE" if boolval else "FALSE")
$ cat myapp/BUILD
load("//myapp:defs.bzl", "my_boolean_macro")
my_boolean_macro(
boolval = select({
"//tools/target_cpu:x86": True,
"//tools/target_cpu:ppc": False,
}),
)
$ bazel build //myapp:all --cpu=x86
DEBUG: /myworkspace/myapp/defs.bzl:4:3: TRUE.
$ bazel build //mypro:all --cpu=ppc
DEBUG: /myworkspace/myapp/defs.bzl:4:3: TRUE.
הסיבה לכך היא שרכיבי המאקרו לא מבינים את התוכן של select()
.
מה שהם באמת בודקים הוא האובייקט select()
. בהתאם
לכללי העיצוב של Python, כל האובייקטים מלבד מספר קטן של חריגים
מוחזרים אוטומטית באופן אוטומטי.
האם אפשר לקרוא את Select() כמו הכתבה?
פקודות מאקרו יכולות't להעריך את הבחירות כי המאקרו מעריך לפני
ש-Bazel יודעת מה הפרמטרים של שורת הפקודה של Build' האם הם יכולים לפחות לקרוא
את המילון של select()
' לדוגמה, כדי להוסיף סיומת לכל ערך?
באופן עקרוני, זה אפשרי, אבל עדיין לא תכונה של Bazel.
מה שאפשר לעשות היום הוא להכין מילון ישר ואז להזין אותו
select()
:
$ cat myapp/defs.bzl
def selecty_genrule(name, select_cmd):
for key in select_cmd.keys():
select_cmd[key] += " WITH SUFFIX"
native.genrule(
name = name,
outs = [name + ".out"],
srcs = [],
cmd = "echo " + select(select_cmd + {"//conditions:default": "default"})
+ " > $@"
)
$ cat myapp/BUILD
selecty_genrule(
name = "selecty",
select_cmd = {
"//tools/target_cpu:x86": "x86 mode",
},
)
$ bazel build //testapp:selecty --cpu=x86 && cat bazel-genfiles/testapp/selecty.out
x86 mode WITH SUFFIX
אם ברצונך לתמוך ב-select()
וגם בסוגים מקומיים, ניתן לעשות זאת:
$ cat myapp/defs.bzl
def selecty_genrule(name, select_cmd):
cmd_suffix = ""
if type(select_cmd) == "string":
cmd_suffix = select_cmd + " WITH SUFFIX"
elif type(select_cmd) == "dict":
for key in select_cmd.keys():
select_cmd[key] += " WITH SUFFIX"
cmd_suffix = select(select_cmd + {"//conditions:default": "default"})
native.genrule(
name = name,
outs = [name + ".out"],
srcs = [],
cmd = "echo " + cmd_suffix + "> $@",
)
למה't select() פועל עם bind()?
כי bind()
הוא כלל WORKSPACE, ולא כלל BUILD.
לכללי Workspace אין הגדרה ספציפית, והם לא מוערכים
בדיוק כמו כללי BUILD. לכן, select()
ב-bind()
לא יכול להעריך
בפועל לסניף מסוים.
במקום זאת, יש להשתמש בשיטה alias()
, עם מאפיין select()
במאפיין actual
, כדי לבצע את ההגדרה הזו של זמן הריצה. פעולה זו
פועלת כהלכה, מפני ש-alias()
הוא כלל BUILD ומוערך באמצעות הגדרה ספציפית.
אפשר גם להגדיר נקודת יעד של bind()
ל-alias()
, אם צריך.
$ cat WORKSPACE
workspace(name = "myapp")
bind(name = "openssl", actual = "//:ssl")
http_archive(name = "alternative", ...)
http_archive(name = "boringssl", ...)
$ cat BUILD
config_setting(
name = "alt_ssl",
define_values = {
"ssl_library": "alternative",
},
)
alias(
name = "ssl",
actual = select({
"//:alt_ssl": "@alternative//:ssl",
"//conditions:default": "@boringssl//:ssl",
}),
)
בהגדרה הזו אפשר לעבור את --define ssl_library=alternative
, וכל יעד
התלוי ב-//:ssl
או ב-//external:ssl
יראה את החלופה
שממוקמת בכתובת @alternative//:ssl
.
למה' select() שלי בוחר למה לצפות?
אם הפונקציה //myapp:foo
כוללת select()
שלא בוחר את התנאי הצפוי,
משתמשים ב-query וב-bazel config
לניפוי באגים:
אם היעד הוא ברמה העליונה ובנייה ב-//myapp:foo
, יש להריץ:
$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)
אם אתה בונה יעד נוסף כלשהו מסוג //bar
שתלוי ב-
myapp:foo במקום כלשהו בגרף שלו, יש להריץ:
$ bazel cquery 'somepath(//bar, //myapp:foo)' <desired build flags>
//bar:bar (3ag3193fee94a2)
//bar:intermediate_dep (12e23b9a2b534a)
//myapp:foo (12e23b9a2b534a)
ה-(12e23b9a2b534a)
שליד //myapp:foo
הוא גיבוב של ההגדרה שמפתורת את select()
&//myapp:foo
. אפשר לבדוק את הערכים שלו באמצעות bazel config
:
$ bazel config 12e23b9a2b534a
BuildConfigurationValue 12e23b9a2b534a
Fragment com.google.devtools.build.lib.analysis.config.CoreOptions {
cpu: darwin
compilation_mode: fastbuild
...
}
Fragment com.google.devtools.build.lib.rules.cpp.CppOptions {
linkopt: [-Dfoo=bar]
...
}
...
לאחר מכן, משווים את הפלט הזה להגדרות הצפויות בכל config_setting
.
ייתכן ש-//myapp:foo
קיים בתצורות שונות באותו בניין. לקבלת הנחיות לשימוש ב-somepath
לקבלת הקוד המתאים, אפשר לעיין במסמכי השאילתה.
למה select()
לא פועל עם פלטפורמות?
Bazel אינה תומכת במאפיינים הניתנים להגדרה על ידי בדיקה אם פלטפורמה מסוימת היא פלטפורמת היעד, מכיוון שהסמנטיקה שלה לא ברורה.
למשל:
platform(
name = "x86_linux_platform",
constraint_values = [
"@platforms//cpu:x86",
"@platforms//os:linux",
],
)
cc_library(
name = "lib",
srcs = [...],
linkopts = select({
":x86_linux_platform": ["--enable_x86_optimizations"],
"//conditions:default": [],
}),
)
בקובץ הזה, BUILD
(באיזו select()
) צריך להשתמש אם בפלטפורמת היעד יש אילוצים של @platforms//cpu:x86
וגם של @platforms//os:linux
, אבל לא מוגדר כאן :x86_linux_platform
. למחבר של קובץ ה-BUILD
ולמשתמש שהגדיר
את הפלטפורמה הנפרדת יכולים להיות רעיונות שונים.
מה עליי לעשות במקום זאת?
במקום זאת, צריך להגדיר config_setting
שתואם לכל הפלטפורמה שיש לה את האילוצים הבאים:
config_setting(
name = "is_x86_linux",
constraint_values = [
"@platforms//cpu:x86",
"@platforms//os:linux",
],
)
cc_library(
name = "lib",
srcs = [...],
linkopts = select({
":is_x86_linux": ["--enable_x86_optimizations"],
"//conditions:default": [],
}),
)
התהליך הזה מגדיר סמנטיקה מסוימת, ומבהיר למשתמשים אילו פלטפורמות עונות על התנאים הרצויים.
מה אם אני באמת רוצה select
בפלטפורמה?
אם באופן ספציפי יש צורך לבדוק את הפלטפורמה, אפשר לשנות את הערך של הסימון --platforms
בconfig_setting
:
config_setting(
name = "is_specific_x86_linux_platform",
values = {
"platforms": ["//package:x86_linux_platform"],
},
)
cc_library(
name = "lib",
srcs = [...],
linkopts = select({
":is_specific_x86_linux_platform": ["--enable_x86_optimizations"],
"//conditions:default": [],
}),
)
הצוות של Bazel לא מאשר זאת; הוא מגביל את ה-build ומבלבל את המשתמשים כאשר התנאי הצפוי לא תואם.