แอตทริบิวต์ที่กำหนดค่าได้ หรือที่รู้จักกันโดยทั่วไปในชื่อ 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()
เป็นกฎ WORKSPACE ไม่ใช่กฎ BUILD
กฎพื้นที่ทำงานไม่มีการกําหนดค่าที่เจาะจง และไม่มีการประเมินใน
ในลักษณะเดียวกับกฎ 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()
ที่ไม่เลือกเงื่อนไขที่คุณคาดหวัง
ใช้ 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 ไม่สนับสนุนการทำงานนี้ ก็จะเป็นการจำกัดการสร้าง ทำให้ผู้ใช้สับสนเมื่อเงื่อนไขที่คาดไว้ไม่ตรงกัน