แอตทริบิวต์ที่กำหนดค่าได้ หรือที่รู้จักกันโดยทั่วไปในชื่อ select()
เป็นฟีเจอร์ Bazel ที่ช่วยให้ผู้ใช้เปิด/ปิดค่า
ของแอตทริบิวต์กฎบิลด์ที่บรรทัดคำสั่ง
เช่น ใช้สำหรับไลบรารีหลายแพลตฟอร์มที่ เลือกการใช้งานที่เหมาะสมสำหรับสถาปัตยกรรม ไบนารีที่ปรับแต่งได้ซึ่งปรับแต่งได้ในเวลาบิลด์
ตัวอย่าง
# 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
ที่ "เลือก" ของตนเองตามธงที่
บรรทัดคำสั่ง กล่าวโดยละเอียดคือ deps
จะมีคุณสมบัติต่อไปนี้
คำสั่ง | deps = |
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()
ทำหน้าที่เป็นตัวยึดตำแหน่งสำหรับค่าที่จะเลือกโดยอิงตาม
เงื่อนไขการกำหนดค่า ซึ่งเป็นป้ายกำกับที่อ้างอิง config_setting
เป้าหมาย เมื่อใช้ select()
ในแอตทริบิวต์ configurable แอตทริบิวต์
ใช้ค่าที่แตกต่างกันได้อย่างมีประสิทธิภาพเมื่อมีการเก็บเงื่อนไขที่แตกต่างกัน
รายการที่ตรงกันต้องชัดเจน หากมีหลายเงื่อนไขตรงกัน ระบบจะระบุเงื่อนไขอย่างใดอย่างหนึ่งต่อไปนี้
* ทุกค่าจะมีค่าเหมือนกัน ตัวอย่างเช่น เมื่อเรียกใช้บน Linux x86 สิ่งนี้จะไม่ไม่ชัดเจน
{"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}
เนื่องจากทั้ง 2 สาขาแก้ไขเป็น "สวัสดี"
* values
ของ 1 คือชุดขั้นสูงที่เข้มงวดกว่าของผู้อื่น เช่น values = {"cpu": "x86", "compilation_mode": "dbg"}
เป็นความเชี่ยวชาญพิเศษของ values = {"cpu": "x86"}
ที่เห็นได้ชัด
เงื่อนไขในตัว //conditions:default
จะจับคู่โดยอัตโนมัติเมื่อ
ที่ไม่มีอะไรทำได้
แม้ว่าตัวอย่างนี้จะใช้ deps
แต่ select()
ก็ทำงานได้ดีใน srcs
เช่นกัน
resources
, cmd
และแอตทริบิวต์อื่นๆ ส่วนใหญ่ มีแอตทริบิวต์เพียงไม่กี่รายการ
ไม่สามารถกำหนดค่าได้ และมีคำอธิบายประกอบอย่างชัดเจน ตัวอย่างเช่น
ของ config_setting
เอง
กำหนดค่าแอตทริบิวต์ values
ไม่ได้
select()
และทรัพยากร Dependency
แอตทริบิวต์บางรายการเปลี่ยนพารามิเตอร์บิลด์สำหรับทรัพยากร Dependency แบบทรานซิทีฟทั้งหมด
ภายใต้เป้าหมาย เช่น tools
ของ genrule
เปลี่ยน --cpu
เป็น CPU ของ
เครื่องที่ใช้งาน Bazel (ซึ่งอาจมีการคอมไพล์แบบข้ามระบบ)
มากกว่า 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
จะเชื่อมโยงบิลด์กับ g_arm.src
, tool1
และ
x86tool.cc
select
ทั้ง 2 รายการที่แนบกับ my_genrule
ใช้ของ my_genrule
พารามิเตอร์บิลด์ ซึ่งรวมถึง --cpu=arm
การเปลี่ยนแปลงแอตทริบิวต์ tools
--cpu
ไปยัง x86
สำหรับ tool1
และทรัพยากร Dependency แบบทรานซิทีฟ select
บน
tool1
ใช้พารามิเตอร์บิลด์ของ tool1
ซึ่งรวมถึง --cpu=x86
เงื่อนไขการกำหนดค่า
แต่ละคีย์ในแอตทริบิวต์ที่กำหนดค่าได้คือการอ้างอิงป้ายกำกับไปยัง
config_setting
หรือ
constraint_value
config_setting
เป็นเพียงคอลเล็กชันของ
การตั้งค่าแฟล็กบรรทัดคำสั่งที่คาดไว้ เมื่อรวมรายการเหล่านี้ไว้ในเป้าหมาย
"มาตรฐาน" ในการดูแลรักษาง่าย เงื่อนไขที่ผู้ใช้สามารถอ้างอิงได้จากหลายแหล่ง
constraint_value
รองรับลักษณะการทำงานแบบหลายแพลตฟอร์ม
แฟล็กในตัว
Flag อย่างเช่น --cpu
มีอยู่ใน Bazel อยู่แล้ว ซึ่งเป็นเครื่องมือที่เข้าใจได้อยู่แล้ว
สําหรับทุกบิลด์ในทุกโปรเจ็กต์ สิ่งที่ระบุด้วย
ของ config_setting
แอตทริบิวต์ของ 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
รองรับเฉพาะ Flag ที่ส่งผลต่อลักษณะการทำงานเป้าหมายเท่านั้น ตัวอย่างเช่น
ไม่อนุญาต--show_progress
เนื่องจาก
มีผลต่อความคืบหน้าของรายงาน Bazel ของผู้ใช้เท่านั้น เป้าหมายใช้ข้อมูลดังกล่าวไม่ได้
ตั้งค่าสถานะเพื่อสร้างผลลัพธ์ ชุดของแฟล็กที่รองรับไม่ถูกต้อง
จัดทำเป็นเอกสาร ในทางปฏิบัติ การรายงานปัญหาส่วนใหญ่ที่ "มีเหตุผล" งาน
การแจ้งที่กำหนดเอง
คุณสร้าง Flag เฉพาะโปรเจ็กต์ของคุณเองได้ด้วย การตั้งค่าบิลด์ของ Starlark แฟล็กเหล่านี้แตกต่างจาก Flag ในตัว เป็นเป้าหมายของบิลด์ ดังนั้น Bazel จะอ้างอิงเป้าหมายดังกล่าวด้วยป้ายกำกับเป้าหมาย
ซึ่งจะทริกเกอร์ด้วยแท็กของ config_setting
flag_values
แอตทริบิวต์:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
ลักษณะการทำงานจะเหมือนกับแฟล็กในตัว โปรดดูที่นี่ สำหรับตัวอย่างที่ใช้ได้จริง
--define
เป็นไวยากรณ์แบบเดิมอีกแบบหนึ่งสำหรับ Flag ที่กำหนดเอง (เช่น
--define foo=bar
) ซึ่งสามารถแสดงใน
แอตทริบิวต์ values
(values = {"define": "foo=bar"}
) หรือ
แอตทริบิวต์ define_values
(define_values = {"foo": "bar"}
) รองรับ --define
สำหรับย้อนกลับเท่านั้น
ความสามารถในการใช้งานร่วมกัน เลือกใช้การตั้งค่าบิลด์ของ Starlark ทุกครั้งที่ทำได้
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
ตรงกันเมื่อเงื่อนไขตรงกันหลายรายการ โปรดพิจารณาและ
การผูกข้อมูล
การทำเชนแบบ OR
ลองพิจารณาสิ่งเหล่านี้
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"],
}),
)
ซึ่งทำให้จัดการทรัพยากร Dependency ได้ง่ายขึ้น แต่ก็ยังก่อให้เกิดความไม่จำเป็น ข้อมูลที่ซ้ำกัน
โปรดใช้ตัวเลือกต่อไปนี้เพื่อรับการสนับสนุนโดยตรงเพิ่มเติม
selects.with_or
with_or
ใน Skylib
selects
โมดูลรองรับ OR
ing เงื่อนไขโดยตรงภายใน 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
selects
โมดูลรองรับ OR
ing config_setting
หลายรายการ:
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"],
}),
)
เป้าหมายต่างๆ สามารถแชร์ :config1_or_2
ร่วมกัน ซึ่งแตกต่างจาก selects.with_or
แอตทริบิวต์ต่างๆ
การจับคู่ดังกล่าวเป็นข้อผิดพลาดสำหรับหลายเงื่อนไข เว้นแต่ว่าเงื่อนไขใดเงื่อนไขหนึ่งไม่ชัดเจน "ความเชี่ยวชาญพิเศษ" รายการอื่นหรือทั้งหมดมีค่าเดียวกัน ดูรายละเอียดที่นี่
และการทำสายโซ่
หากต้องการให้ Branch ของ 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"],
}),
)
config_setting
ที่มีอยู่จะAND
โดยตรงไม่ได้ ซึ่งต่างจากการทำเชนแบบ OR
ภายใน 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"}, ...)
โดยมีเหตุผล 2 ข้อ
อย่างแรก มาโครที่จำเป็นต้องทราบเส้นทางที่ select
จะเลือกใช้งานไม่ได้
เนื่องจากมาโครจะได้รับการประเมินในระยะการโหลดของ Bazel
ซึ่งเกิดขึ้นก่อนที่จะทราบค่าแฟล็ก
นี่คือข้อจำกัดหลักด้านการออกแบบ Bazel ที่มีแนวโน้มว่าจะไม่เปลี่ยนแปลงในเร็วๆ นี้
อย่างที่ 2 มาโครที่ต้องการแค่เพื่อทำซ้ำบนเส้นทาง select
ทั้งหมด
ในทางเทคนิคแล้วไม่มี UI ที่สอดคล้องกัน จำเป็นต้องมีการออกแบบเพิ่มเติมเพื่อเปลี่ยน
นี้
คำค้นหาและ BigQuery ของ Bazel
Bazel query
ดำเนินงานอยู่เหนือ Bazel
ระยะการโหลด
ซึ่งหมายความว่าระบบจะไม่ทราบว่าบรรทัดคำสั่งใดใช้แฟล็กเป้าหมาย เนื่องจาก
จะไม่มีการประเมินแฟล็กจนกระทั่งภายหลังในการสร้าง (ใน
analytics)
จึงไม่สามารถระบุได้ว่าจะเลือก select()
Branch ใด
Bazel cquery
ทำงานหลังจากขั้นตอนการวิเคราะห์ของ Bazel จึง
ข้อมูลทั้งหมดนี้และสามารถแก้ปัญหา 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
มีค่า Dependency ของ :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() ทำงานได้ในกฎหรือไม่ ดูความเข้ากันได้ของกฎสำหรับ รายละเอียด
ปัญหาหลักสำหรับคำถามนี้โดยทั่วไปคือ 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({
"//third_party/bazel_platforms/cpu:x86_32": "first string",
"//third_party/bazel_platforms/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({
"//third_party/bazel_platforms/cpu:x86_32": "first string",
"//third_party/bazel_platforms/cpu:ppc": "other string",
}),
)
การสร้างล้มเหลวเนื่องจาก sad_macro
ประมวลผล select()
ไม่ได้
$ 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 อ่านแฟล็กบรรทัดคำสั่งของบิลด์ ซึ่งหมายความว่ามีไม่เพียงพอ ในการประเมิน select()s
อย่างไรก็ตาม มาโครสามารถส่ง select()
เป็น BLOB ที่คลุมเครือไปยังกฎได้ดังนี้
# 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()
ไม่ได้ จากทุกความพยายาม
มักจะทำให้เกิดข้อผิดพลาด เช่น
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({
"//third_party/bazel_platforms/cpu:x86_32": True,
"//third_party/bazel_platforms/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()
เอง ตามที่
การออกแบบ Pythonic
ออบเจ็กต์ทั้งหมดนอกเหนือจากข้อยกเว้นที่น้อยมาก
แสดงค่า "จริง" โดยอัตโนมัติ
ฉันสามารถอ่าน select() อย่างเช่นคำสั่งได้ไหม
มาโครไม่สามารถประเมินรายการที่เลือกได้เนื่องจากมาโครประเมินก่อน
Bazel รู้ว่าพารามิเตอร์บรรทัดคำสั่งของบิลด์คืออะไร อย่างน้อยคนเหล่านี้สามารถอ่าน
ในพจนานุกรมของ 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 = {
"//third_party/bazel_platforms/cpu:x86_32": "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 + "> $@",
)
เหตุใด Select() จึงไม่ทำงานร่วมกับ bind()
ก่อนอื่น โปรดอย่าใช้ bind()
ซึ่งเลิกใช้งานแล้วเพื่อใช้ alias()
แทน
คำตอบทางเทคนิคคือ bind()
เป็นที่เก็บ
กฎ ไม่ใช่กฎ BUILD
กฎที่เก็บไม่มีการกำหนดค่าที่เจาะจงและไม่มีการประเมินใน
ในลักษณะเดียวกับกฎ BUILD ดังนั้น select()
ใน bind()
ต้องไม่มี
ประเมินให้กับฝ่ายใดฝ่ายหนึ่งโดยเฉพาะ
คุณควรใช้ alias()
ที่มี select()
ใน
แอตทริบิวต์ actual
เพื่อทำการพิจารณารันไทม์ประเภทนี้ ช่วงเวลานี้
ทำงานได้อย่างถูกต้อง เนื่องจาก alias()
เป็นกฎ BUILD และมีการประเมินด้วยแอตทริบิวต์
การกำหนดค่าเฉพาะ
$ 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
แต่จริงๆ แล้ว โปรดหยุดใช้ bind()
เหตุใด select() ของฉันจึงไม่เลือกสิ่งที่คาดหวัง
หาก //myapp:foo
มี select()
ที่ไม่เลือกเงื่อนไขที่คุณคาดหวัง
ใช้ cquery และ 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
อาจมีการกำหนดค่าต่างกันในบิลด์เดียวกัน โปรดดู
เอกสาร cquery สำหรับแนวทางในการใช้ 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 ไม่สนับสนุนการทำงานนี้ ก็จะเป็นการจำกัดการสร้าง ทำให้ผู้ใช้สับสนเมื่อเงื่อนไขที่คาดไว้ไม่ตรงกัน