แพลตฟอร์ม

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

การประมาณสภาพแวดล้อมเป็นแพลตฟอร์มช่วยให้ Bazel เลือกเครื่องมือที่เหมาะสมโดยอัตโนมัติในการสร้างการดำเนินการ คุณใช้แพลตฟอร์มร่วมกับกฎ 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 และค่าที่เป็นไปได้ของมิติข้อมูลนั้นได้อย่างมีประสิทธิภาพ ตัวอย่างเช่น ข้อมูลโค้ดต่อไปนี้ของไฟล์ 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 จัดส่งตามคำจำกัดความของแพลตฟอร์มพิเศษต่อไปนี้ @local_config_platform//:host ค่านี้คือค่าแพลตฟอร์มของโฮสต์ที่ระบบตรวจพบโดยอัตโนมัติ ซึ่งแสดงถึงแพลตฟอร์มที่ Bazel ทำงานอยู่โดยอัตโนมัติ

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

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

  • --host_platform - ค่าเริ่มต้นคือ @bazel_tools//platforms:host_platform
  • --platforms - ค่าเริ่มต้นคือ @bazel_tools//platforms:target_platform

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

เมื่อสร้างสำหรับแพลตฟอร์มเป้าหมายหนึ่งๆ คุณควรข้ามเป้าหมายที่จะไม่ทำงานบนแพลตฟอร์มนั้นเลย เช่น ไดรเวอร์อุปกรณ์ 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 บิตและใช้ร่วมกับ Windows อื่นๆ ไม่ได้ ซึ่งความไม่เข้ากันนี้จะเกิดขึ้น เป้าหมายที่เปลี่ยนไปยังเป้าหมายที่เข้ากันไม่ได้จะถือว่าเข้ากันไม่ได้

เป้าหมายจะถูกข้ามเมื่อใด

ระบบจะข้ามเป้าหมายเมื่อถือว่าใช้ร่วมกันไม่ได้และรวมอยู่ในบิลด์โดยเป็นส่วนหนึ่งของการขยายรูปแบบเป้าหมาย เช่น การเรียกใช้ 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

มีข้อจำกัดที่ชัดเจนมากขึ้น

เพื่อการแสดงข้อจำกัดได้อย่างยืดหยุ่นมากขึ้น ให้ใช้@platforms//:incompatible constraint_valueที่ไม่มีแพลตฟอร์มให้รองรับ

ใช้ select() ร่วมกับ @platforms//:incompatible เพื่อแสดงข้อจํากัดที่ซับซ้อนมากขึ้น ตัวอย่างเช่น ใช้ตรรกะ "หรือ" เพื่อนำตรรกะ "หรือ" พื้นฐานไปใช้ รายการต่อไปนี้หมายถึงไลบรารีที่ใช้ร่วมกับ 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 ไม่ได้อยู่ในแพลตฟอร์มใดๆ เป้าหมายดังกล่าวจึงถือว่าเข้ากันไม่ได้

หากต้องการทำให้ข้อจำกัดอ่านง่ายขึ้น ให้ใช้ selects.with_or() ของ skylib

คุณจะแสดงความเข้ากันได้แบบผกผันได้ในลักษณะเดียวกัน ตัวอย่างต่อไปนี้อธิบายไลบรารีที่เข้ากันได้กับทุกอย่าง ยกเว้นสำหรับ 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 ในรูปแบบเอาต์พุต Starlark ของ bazel cquery เพื่อแยกแยะเป้าหมายที่เข้ากันไม่ได้ออกจากเป้าหมายที่เข้ากันได้

สามารถใช้เพื่อกรองเป้าหมายที่ใช้ร่วมกันไม่ได้ออก ตัวอย่างด้านล่างจะพิมพ์เฉพาะป้ายกำกับสำหรับเป้าหมายที่เข้ากันได้เท่านั้น เป้าหมายที่เข้ากันไม่ได้ จะไม่ถูกพิมพ์

$ cat example.cquery

def format(target):
  if "IncompatiblePlatformProvider" not in providers(target):
    return target.label
  return ""


$ bazel cquery //... --output=starlark --starlark:file=example.cquery

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

เป้าหมายใช้ร่วมกันไม่ได้ เพิกเฉยต่อข้อจำกัดระดับการเข้าถึง