บทนำ
Bazel สามารถสร้างและทดสอบโค้ดในฮาร์ดแวร์ ระบบปฏิบัติการ และการกำหนดค่าระบบที่หลากหลาย ซึ่งอาจเกี่ยวข้องกับเครื่องมือบิลด์เวอร์ชันต่างๆ เช่น Linker และคอมไพเลอร์ Bazel จึงมีแนวคิดเรื่อง ข้อจำกัดและ แพลตฟอร์มเพื่อช่วยจัดการความซับซ้อนนี้
ข้อจำกัดคือพร็อพเพอร์ตี้ที่แยกความแตกต่างของเครื่องบิลด์หรือเครื่องที่ใช้จริง ข้อจำกัดทั่วไป ได้แก่ สถาปัตยกรรม CPU, การมีหรือไม่มี GPU หรือคอมไพเลอร์เวอร์ชันที่ติดตั้งในเครื่อง แต่ข้อจำกัดอาจเป็น สิ่งใดก็ได้ที่แยกความแตกต่างของเครื่องได้อย่างมีความหมายเมื่อจัดระเบียบงานบิลด์
แพลตฟอร์มคือชุดข้อจำกัดที่ระบุเครื่องที่สมบูรณ์ Bazel ใช้แนวคิดนี้เพื่อให้ผู้พัฒนาเลือกเครื่องที่ต้องการสร้าง เครื่องที่ควรเรียกใช้การดำเนินการคอมไพล์และทดสอบ รวมถึง Toolchain ที่การดำเนินการบิลด์ควรคอมไพล์ด้วย
นอกจากนี้ ผู้พัฒนายังใช้ข้อจำกัดเพื่อ เลือก
พร็อพเพอร์ตี้หรือการขึ้นต่อกันที่กำหนดเองในกฎบิลด์ได้ด้วย เช่น "ใช้
src_arm.cc เมื่อบิลด์กำหนดเป้าหมายเป็นเครื่อง Arm"
ประเภทแพลตฟอร์ม
Bazel ตระหนักถึงบทบาท 3 อย่างที่แพลตฟอร์มอาจมี ดังนี้
- โฮสต์ - แพลตฟอร์มที่ Bazel ทำงานอยู่
- การดำเนินการ - แพลตฟอร์มที่เรียกใช้การดำเนินการคอมไพล์เพื่อสร้างเอาต์พุตบิลด์
- เป้าหมาย - แพลตฟอร์มที่โค้ดที่กำลังสร้างควรทำงาน
โดยทั่วไปบิลด์มีความสัมพันธ์กับแพลตฟอร์ม 3 ประเภท ดังนี้
บิลด์แพลตฟอร์มเดียว - แพลตฟอร์มโฮสต์ การดำเนินการ และเป้าหมายเป็นแพลตฟอร์มเดียวกัน เช่น การสร้างในเครื่องของนักพัฒนาซอฟต์แวร์โดยไม่มีการดำเนินการระยะไกล แล้วเรียกใช้ไบนารีที่สร้างขึ้นในเครื่องเดียวกัน
บิลด์การคอมไพล์ข้ามแพลตฟอร์ม - แพลตฟอร์มโฮสต์และการดำเนินการเป็นแพลตฟอร์มเดียวกัน แต่แพลตฟอร์มเป้าหมายเป็นแพลตฟอร์มอื่น เช่น การสร้างแอป iOS ใน Macbook Pro โดยไม่มีการดำเนินการระยะไกล
บิลด์หลายแพลตฟอร์ม - แพลตฟอร์มโฮสต์ การดำเนินการ และเป้าหมายเป็นแพลตฟอร์มอื่นทั้งหมด เช่น การสร้างแอป iOS ใน Macbook Pro และใช้เครื่อง Linux ระยะไกลเพื่อคอมไพล์การดำเนินการ C++ ที่ไม่จำเป็นต้องใช้ Xcode
การระบุแพลตฟอร์ม
วิธีที่ผู้พัฒนาใช้แพลตฟอร์มกันมากที่สุดคือการระบุเครื่องเป้าหมายที่ต้องการด้วยแฟล็ก --platforms ดังนี้
$ bazel build //:my_linux_app --platforms=//myplatforms:linux_x86
โดยทั่วไปองค์กรจะดูแลรักษาคำจำกัดความของแพลตฟอร์มของตนเอง เนื่องจากมีการตั้งค่าเครื่องบิลด์ที่แตกต่างกันระหว่างองค์กร
เมื่อไม่ได้ตั้งค่า --platforms ระบบจะตั้งค่าเริ่มต้นเป็น @platforms//host ซึ่งกำหนดไว้เป็นพิเศษเพื่อตรวจหาพร็อพเพอร์ตี้ของระบบปฏิบัติการและ CPU ของเครื่องโฮสต์โดยอัตโนมัติ เพื่อให้บิลด์กำหนดเป้าหมายเป็นเครื่องเดียวกับที่ Bazel ทำงานอยู่ กฎบิลด์สามารถ
เลือกพร็อพเพอร์ตี้เหล่านี้ได้ด้วยข้อจำกัด
@platforms/os และ
@platforms/cpu
ข้อจำกัดและแพลตฟอร์มที่มีประโยชน์โดยทั่วไป
ทีม Bazel ดูแลรักษาที่เก็บข้อมูลที่มีคำจำกัดความของข้อจำกัดสำหรับสถาปัตยกรรม CPU และระบบปฏิบัติการที่ได้รับความนิยมมากที่สุด เพื่อให้ระบบนิเวศมีความสอดคล้องกัน ซึ่งทั้งหมดกำหนดไว้ใน https://github.com/bazelbuild/platforms
Bazel มาพร้อมกับคำจำกัดความของแพลตฟอร์มพิเศษต่อไปนี้ @platforms//host (กำหนดชื่อแทนเป็น @bazel_tools//tools:host_platform) ซึ่งจะตรวจหาพร็อพเพอร์ตี้ของระบบปฏิบัติการและ CPU ของเครื่องที่ Bazel ทำงานอยู่โดยอัตโนมัติ
การกำหนดข้อจำกัด
ข้อจำกัดได้รับการสร้างแบบจำลองด้วยconstraint_setting และ
constraint_value กฎบิลด์
constraint_setting ประกาศประเภทพร็อพเพอร์ตี้ เช่น
constraint_setting(name = "cpu")
constraint_value ประกาศค่าที่เป็นไปได้สำหรับพร็อพเพอร์ตี้นั้น
constraint_value(
name = "x86",
constraint_setting = ":cpu"
)
คุณสามารถอ้างอิงค่าเหล่านี้เป็นป้ายกำกับเมื่อกำหนดแพลตฟอร์มหรือปรับแต่งกฎบิลด์ในแพลตฟอร์ม หากกำหนดตัวอย่างข้างต้นไว้ใน cpus/BUILD คุณสามารถอ้างอิง
ข้อจำกัด x86 เป็น //cpus:x86
หากการมองเห็นอนุญาต คุณสามารถขยาย constraint_setting ที่มีอยู่ได้โดยกำหนดค่าของคุณเองสำหรับข้อจำกัดนั้น
การกำหนดแพลตฟอร์ม
กฎบิลด์ platform
กำหนดแพลตฟอร์มเป็นคอลเล็กชันของ constraint_value ดังนี้
platform(
name = "linux_x86",
constraint_values = [
"//oses:linux",
"//cpus:x86",
],
)
ซึ่งสร้างแบบจำลองเครื่องที่ต้องมีทั้งข้อจำกัด //oses:linux และ //cpus:x86
แพลตฟอร์มอาจมี constraint_value เพียงรายการเดียวสำหรับ constraint_setting ที่กำหนด
ซึ่งหมายความว่าแพลตฟอร์มจะมี CPU 2 รายการไม่ได้ เว้นแต่คุณจะสร้างประเภท constraint_setting อื่นเพื่อสร้างแบบจำลองค่าที่ 2
การข้ามเป้าหมายที่ไม่เข้ากัน
เมื่อสร้างแพลตฟอร์มเป้าหมายที่เฉพาะเจาะจง คุณมักจะต้องการข้ามเป้าหมายที่จะไม่ทำงานในแพลตฟอร์มนั้น เช่น ไดรเวอร์อุปกรณ์ Windows มีแนวโน้มที่จะสร้างข้อผิดพลาดของคอมไพเลอร์จำนวนมากเมื่อสร้างในเครื่อง Linux ด้วย //... ใช้แอตทริบิวต์
target_compatible_with
เพื่อบอก Bazel ว่าโค้ดของคุณมีข้อจำกัดของแพลตฟอร์มเป้าหมายใด
การใช้แอตทริบิวต์นี้ที่ง่ายที่สุดคือการจำกัดเป้าหมายไว้ที่แพลตฟอร์มเดียว
ระบบจะไม่สร้างเป้าหมายสำหรับแพลตฟอร์มที่ไม่เป็นไปตามข้อจำกัดทั้งหมด ตัวอย่างต่อไปนี้จำกัด win_driver_lib.cc ไว้ที่ Windows 64 บิต
cc_library(
name = "win_driver_lib",
srcs = ["win_driver_lib.cc"],
target_compatible_with = [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
],
)
:win_driver_lib เข้ากันได้ เฉพาะกับการสร้างด้วย Windows 64 บิต และ
ไม่เข้ากันกับแพลตฟอร์มอื่นๆ การไม่เข้ากันเป็นแบบถ่ายทอด ระบบจะถือว่าเป้าหมายใดก็ตามที่ขึ้นต่อกันแบบถ่ายทอดกับเป้าหมายที่ไม่เข้ากันนั้นไม่เข้ากันด้วย
ระบบจะข้ามเป้าหมายเมื่อใด
ระบบจะข้ามเป้าหมายเมื่อถือว่าเป้าหมายไม่เข้ากันและรวมอยู่ในบิลด์เป็นส่วนหนึ่งของการขยายรูปแบบเป้าหมาย เช่น การเรียกใช้ 2 รายการต่อไปนี้จะข้ามเป้าหมายที่ไม่เข้ากันที่พบในการขยายรูปแบบเป้าหมาย
$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all
ระบบจะข้ามการทดสอบที่ไม่เข้ากันใน test_suite ในลักษณะเดียวกันหากระบุ test_suite ในบรรทัดคำสั่งด้วย
--expand_test_suites
กล่าวอีกนัยหนึ่งคือ เป้าหมาย test_suite ในบรรทัดคำสั่งจะทำงานเหมือนกับ :all และ ... การใช้ --noexpand_test_suites จะป้องกันการขยายและทำให้
test_suite เป้าหมายที่มีการทดสอบที่ไม่เข้ากันนั้นไม่เข้ากันด้วย
การระบุเป้าหมายที่ไม่เข้ากันอย่างชัดแจ้งในบรรทัดคำสั่งจะส่งผลให้เกิดข้อความแสดงข้อผิดพลาดและบิลด์ล้มเหลว
$ bazel build --platforms=//:myplatform //:target_incompatible_with_myplatform
...
ERROR: Target //:target_incompatible_with_myplatform is incompatible and cannot be built, but was explicitly requested.
...
FAILED: Build did NOT complete successfully
ระบบจะข้ามเป้าหมายที่ชัดแจ้งซึ่งไม่เข้ากันโดยไม่มีการแจ้งเตือนหากเปิดใช้ --skip_incompatible_explicit_targets
ข้อจำกัดที่แสดงออกได้มากขึ้น
หากต้องการความยืดหยุ่นมากขึ้นในการแสดงข้อจำกัด ให้ใช้
@platforms//:incompatible
constraint_value
ที่ไม่มีแพลตฟอร์มใดเป็นไปตามข้อจำกัด
ใช้ select() ร่วมกับ
@platforms//:incompatible เพื่อแสดงข้อจำกัดที่ซับซ้อนมากขึ้น เช่น ใช้เพื่อใช้ตรรกะ OR พื้นฐาน ตัวอย่างต่อไปนี้จะทำเครื่องหมายไลบรารีว่าเข้ากันได้กับ macOS และ Linux แต่ไม่เข้ากันกับแพลตฟอร์มอื่นๆ
cc_library(
name = "unixish_lib",
srcs = ["unixish_lib.cc"],
target_compatible_with = select({
"@platforms//os:osx": [],
"@platforms//os:linux": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
)
คุณสามารถตีความตัวอย่างข้างต้นได้ดังนี้
- เมื่อกำหนดเป้าหมายเป็น macOS เป้าหมายจะไม่มีข้อจำกัด
- เมื่อกำหนดเป้าหมายเป็น Linux เป้าหมายจะไม่มีข้อจำกัด
- มิเช่นนั้น เป้าหมายจะมีข้อจำกัด
@platforms//:incompatibleเนื่องจาก@platforms//:incompatibleไม่ได้เป็นส่วนหนึ่งของแพลตฟอร์มใดๆ ระบบจึงถือว่าเป้าหมายไม่เข้ากัน
ใช้
skylib's
selects.with_or()เพื่อให้ข้อจำกัดอ่านง่ายขึ้น
คุณสามารถแสดงความเข้ากันได้แบบผกผันในลักษณะที่คล้ายกัน ตัวอย่างต่อไปนี้อธิบายไลบรารีที่เข้ากันได้กับทุกอย่าง ยกเว้น ARM
cc_library(
name = "non_arm_lib",
srcs = ["non_arm_lib.cc"],
target_compatible_with = select({
"@platforms//cpu:arm": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
)
การตรวจหาเป้าหมายที่ไม่เข้ากันโดยใช้ bazel cquery
คุณสามารถใช้
IncompatiblePlatformProvider
ใน bazel cquery's รูปแบบเอาต์พุต Starlark เพื่อแยกเป้าหมายที่ไม่เข้ากันออกจากเป้าหมายที่เข้ากันได้
ซึ่งสามารถใช้เพื่อกรองเป้าหมายที่ไม่เข้ากันออกได้ ตัวอย่างด้านล่างจะพิมพ์เฉพาะป้ายกำกับสำหรับเป้าหมายที่เข้ากันได้ และจะไม่พิมพ์เป้าหมายที่ไม่เข้ากัน
$ cat example.cquery
def format(target):
if "IncompatiblePlatformProvider" not in providers(target):
return target.label
return ""
$ bazel cquery //... --output=starlark --starlark:file=example.cquery