แพลตฟอร์ม

บทนำ

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

ข้อจำกัดคือคุณสมบัติที่โดดเด่นของเครื่องที่ใช้สร้างหรือเครื่องที่ใช้ในการผลิต ข้อจำกัดที่พบบ่อย ได้แก่ สถาปัตยกรรม CPU, มีหรือไม่มี GPU หรือ เวอร์ชันของคอมไพเลอร์ที่ติดตั้งในเครื่อง แต่ข้อจำกัดอาจเป็นอะไรก็ได้ที่ แยกความแตกต่างของเครื่องอย่างมีนัยสำคัญเมื่อจัดระเบียบงานบิลด์

แพลตฟอร์มคือชุดข้อจำกัดที่ระบุเครื่องที่สมบูรณ์ Bazel ใช้แนวคิดนี้เพื่อให้ผู้พัฒนาเลือกเครื่องที่ต้องการสร้าง เครื่องที่ควรเรียกใช้การดำเนินการคอมไพล์และทดสอบ และทูลเชนที่ควรใช้คอมไพล์การดำเนินการสร้าง

นอกจากนี้ นักพัฒนาซอฟต์แวร์ยังใช้ข้อจํากัดเพื่อเลือก พร็อพเพอร์ตี้ที่กําหนดเองหรือการขึ้นต่อกันในกฎการสร้างได้ด้วย เช่น "ใช้ 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"],
    }),
)

ซึ่งตีความได้ดังนี้

  1. เมื่อกำหนดเป้าหมายเป็น macOS เป้าหมายจะไม่มีข้อจำกัด
  2. เมื่อกำหนดเป้าหมายเป็น Linux เป้าหมายจะไม่มีข้อจำกัด
  3. มิเช่นนั้น เป้าหมายจะมีข้อจํากัด @platforms//:incompatible เนื่องจาก @platforms//:incompatible ไม่ได้เป็นส่วนหนึ่งของแพลตฟอร์มใดๆ ระบบจึงถือว่าเป้าหมายไม่เข้ากัน

หากต้องการให้ข้อจำกัดอ่านง่ายขึ้น ให้ใช้ skylib ของ 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รูปแบบเอาต์พุต 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

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

เป้าหมายที่ใช้ร่วมกันไม่ได้ไม่สนใจข้อจำกัดด้านการมองเห็น