แพลตฟอร์ม

Bazel สามารถสร้างและทดสอบโค้ดในฮาร์ดแวร์ ระบบปฏิบัติการ และการกำหนดค่าระบบที่หลากหลาย โดยใช้เครื่องมือสร้างหลายเวอร์ชัน เช่น Linker และ Compiler Bazel มีแนวคิดเรื่อง ข้อจำกัดและ แพลตฟอร์มเพื่อช่วยจัดการความซับซ้อนนี้ ข้อจำกัดคือมิติข้อมูลที่สภาพแวดล้อมการสร้างหรือการผลิตอาจแตกต่างกัน เช่น สถาปัตยกรรม CPU, การมีหรือไม่มี GPU หรือเวอร์ชันของ Compiler ที่ติดตั้งในระบบ แพลตฟอร์มคือชุดตัวเลือกที่มีชื่อสำหรับข้อจำกัดเหล่านี้ ซึ่งแสดงถึงทรัพยากรเฉพาะที่พร้อมใช้งานในสภาพแวดล้อมหนึ่งๆ

การจำลองสภาพแวดล้อมเป็นแพลตฟอร์มจะช่วยให้ Bazel เลือก Toolchain ที่เหมาะสม สำหรับการดำเนินการสร้างได้โดยอัตโนมัติ นอกจากนี้ คุณยังใช้แพลตฟอร์มร่วมกับกฎ config_setting เพื่อเขียนแอตทริบิวต์ที่กำหนดค่าได้ด้วย

Bazel รับรู้บทบาท 3 บทบาทที่แพลตฟอร์มอาจมี ดังนี้

  • โฮสต์ - แพลตฟอร์มที่ Bazel ทำงานอยู่
  • การดำเนินการ - แพลตฟอร์มที่เครื่องมือสร้างดำเนินการสร้างเพื่อสร้างเอาต์พุตระดับกลางและขั้นสุดท้าย
  • เป้าหมาย - แพลตฟอร์มที่เอาต์พุตขั้นสุดท้ายอยู่และทำงาน

Bazel รองรับสถานการณ์การสร้างต่อไปนี้เกี่ยวกับแพลตฟอร์ม

  • การสร้างแพลตฟอร์มเดียว (ค่าเริ่มต้น) - แพลตฟอร์มโฮสต์ การดำเนินการ และเป้าหมายเป็นแพลตฟอร์มเดียวกัน เช่น การสร้างไฟล์ปฏิบัติการ Linux ใน Ubuntu ที่ทำงานบน CPU Intel x64

  • การสร้างการคอมไพล์ข้ามแพลตฟอร์ม \- แพลตฟอร์มโฮสต์และการดำเนินการเป็นแพลตฟอร์มเดียวกัน แต่ แพลตฟอร์มเป้าหมายเป็นแพลตฟอร์มอื่น เช่น การสร้างแอป iOS ใน macOS ที่ทำงานบน MacBook Pro

  • การสร้างหลายแพลตฟอร์ม - แพลตฟอร์มโฮสต์ การดำเนินการ และเป้าหมายเป็นแพลตฟอร์มที่แตกต่างกัน

การกำหนดข้อจำกัดและแพลตฟอร์ม

คุณกำหนดพื้นที่ของตัวเลือกที่เป็นไปได้สำหรับแพลตฟอร์มได้โดยใช้กฎ constraint_setting และ constraint_value ภายในไฟล์ BUILD constraint_setting จะสร้างมิติข้อมูลใหม่ ส่วน constraint_value จะสร้างค่าใหม่สำหรับมิติข้อมูลที่กำหนด ซึ่งทั้ง 2 กฎนี้จะกำหนด Enum และค่าที่เป็นไปได้ของ Enum ได้อย่างมีประสิทธิภาพ ตัวอย่างเช่น ข้อมูลโค้ดต่อไปนี้ของไฟล์ BUILD จะแนะนำข้อจำกัดสำหรับเวอร์ชัน glibc ของระบบที่มีค่าที่เป็นไปได้ 2 ค่า

constraint_setting(name = "glibc_version")

constraint_value(
    name = "glibc_2_25",
    constraint_setting = ":glibc_version",
)

constraint_value(
    name = "glibc_2_26",
    constraint_setting = ":glibc_version",
)

คุณสามารถกำหนดข้อจำกัดและค่าของข้อจำกัดในแพ็กเกจต่างๆ ในพื้นที่ทำงานได้ โดยอ้างอิงตามป้ายกำกับและอยู่ภายใต้การควบคุมระดับการเข้าถึงตามปกติ หากระดับการเข้าถึงอนุญาต คุณสามารถขยายการตั้งค่าข้อจำกัดที่มีอยู่ได้โดยกำหนดค่าของคุณเองสำหรับข้อจำกัดนั้น

กฎ platform จะแนะนำแพลตฟอร์มใหม่ที่มีตัวเลือกค่าข้อจำกัดบางอย่าง ตัวอย่างต่อไปนี้จะสร้างแพลตฟอร์มชื่อ linux_x86 และระบุว่าแพลตฟอร์มนี้อธิบายสภาพแวดล้อมที่ใช้ระบบปฏิบัติการ Linux ในสถาปัตยกรรม x86_64 ที่มี glibc เวอร์ชัน 2.25 (ดูข้อมูลเพิ่มเติมเกี่ยวกับข้อจำกัดในตัวของ Bazel ด้านล่าง)

platform(
    name = "linux_x86",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
        ":glibc_2_25",
    ],
)

ข้อจำกัดและแพลตฟอร์มที่มีประโยชน์โดยทั่วไป

ทีม Bazel ดูแลที่เก็บข้อมูลที่มีคำจำกัดความข้อจำกัดสำหรับสถาปัตยกรรม CPU และระบบปฏิบัติการที่ได้รับความนิยมมากที่สุดเพื่อให้ระบบนิเวศมีความสอดคล้องกัน ซึ่งทั้งหมดอยู่ใน https://github.com/bazelbuild/platforms

Bazel มาพร้อมกับคำจำกัดความแพลตฟอร์มพิเศษต่อไปนี้ @platforms//host (กำหนดชื่อแทนเป็น @bazel_tools//tools:host_platform) ซึ่งเป็นค่าแพลตฟอร์มโฮสต์ที่ตรวจหาโดยอัตโนมัติ โดยแสดงถึงแพลตฟอร์มที่ตรวจหาโดยอัตโนมัติสำหรับระบบที่ Bazel ทำงานอยู่

การระบุแพลตฟอร์มสำหรับบิลด์

คุณสามารถระบุแพลตฟอร์มโฮสต์และแพลตฟอร์มเป้าหมายสำหรับการบิลด์ได้โดยใช้ Flag บรรทัดคำสั่งต่อไปนี้

  • --host_platform - ค่าเริ่มต้นคือ @bazel_tools//tools:host_platform
    • เป้าหมายนี้กำหนดชื่อแทนเป็น @platforms//host ซึ่งได้รับการสนับสนุนโดยกฎของ repo ที่ตรวจหาระบบปฏิบัติการและ CPU ของโฮสต์ แล้วเขียนเป้าหมายแพลตฟอร์ม
    • นอกจากนี้ยังมี @platforms//host:constraints.bzl ซึ่งแสดงอาร์เรย์ที่ชื่อว่า HOST_CONSTRAINTS ซึ่งใช้ในไฟล์ BUILD และ Starlark อื่นๆ ได้
  • --platforms - ค่าเริ่มต้นคือแพลตฟอร์มโฮสต์
    • ซึ่งหมายความว่าเมื่อไม่ได้ตั้งค่าแฟล็กอื่นๆ @platforms//host จะเป็นแพลตฟอร์มเป้าหมาย
    • หากตั้งค่า --host_platform แต่ไม่ได้ตั้งค่า --platforms ค่าของ --host_platform จะเป็นทั้งแพลตฟอร์มโฮสต์และแพลตฟอร์มเป้าหมาย

การข้ามเป้าหมายที่ใช้ร่วมกันไม่ได้

เมื่อสร้างสำหรับแพลตฟอร์มเป้าหมายที่เฉพาะเจาะจง คุณมักจะต้องการข้ามเป้าหมายที่จะไม่ทำงานในแพลตฟอร์มนั้น ตัวอย่างเช่น ไดรเวอร์อุปกรณ์ Windows มีแนวโน้มที่จะสร้างข้อผิดพลาดของ Compiler จำนวนมากเมื่อสร้างในเครื่อง 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"],
    }),
)

คุณสามารถตีความรายการด้านบนได้ดังนี้

  1. เมื่อกำหนดเป้าหมายเป็น macOS เป้าหมายจะไม่มีข้อจำกัด
  2. เมื่อกำหนดเป้าหมายเป็น Linux เป้าหมายจะไม่มีข้อจำกัด
  3. มิเช่นนั้น เป้าหมายจะมีข้อจำกัด @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

ปัญหาที่ทราบ

เป้าหมายที่ใช้ร่วมกันไม่ได้จะละเว้นข้อจำกัดระดับการเข้าถึง