บทนำ
Bazel สามารถสร้างและทดสอบโค้ดในฮาร์ดแวร์ ระบบปฏิบัติการ และการกำหนดค่าระบบต่างๆ ซึ่งอาจเกี่ยวข้องกับเครื่องมือบิลด์เวอร์ชันต่างๆ เช่น Linker และคอมไพเลอร์ Bazel จึงมีแนวคิดเรื่อง ข้อจำกัดและ แพลตฟอร์มเพื่อช่วยจัดการความซับซ้อนนี้
ข้อจำกัดคือพร็อพเพอร์ตี้ที่แยกความแตกต่างของเครื่องบิลด์หรือเครื่องที่ใช้จริง ข้อจำกัดทั่วไป ได้แก่ สถาปัตยกรรม CPU, การมีหรือไม่มี GPU หรือคอมไพเลอร์เวอร์ชันที่ติดตั้งไว้ในเครื่อง แต่ข้อจำกัดอาจเป็น อะไรก็ได้ที่แยกความแตกต่างของเครื่องได้อย่างมีความหมายเมื่อจัดระเบียบงานสร้าง
แพลตฟอร์มคือชุดข้อจำกัดที่ระบุเครื่องที่สมบูรณ์ Bazel ใช้แนวคิดนี้เพื่อให้ผู้พัฒนาเลือกเครื่องที่ต้องการสร้างสำหรับ เครื่องที่ควรเรียกใช้การดำเนินการคอมไพล์และทดสอบ รวมถึง Toolchainที่การดำเนินการสร้างควรคอมไพล์ด้วย
นอกจากนี้ ผู้พัฒนายังใช้ข้อจำกัดเพื่อ เลือก พร็อพเพอร์ตี้ที่กำหนดเองหรือทรัพยากร Dependency ในกฎการบิลด์ได้ด้วย เช่น "ใช้
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/ระบบปฏิบัติการ และ
@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