เชนเครื่องมือ

หน้านี้อธิบายเฟรมเวิร์ก Toolchain ซึ่งเป็นวิธีที่ผู้เขียนกฎใช้แยกตรรกะของกฎออกจากการเลือกเครื่องมือตามแพลตฟอร์ม เราขอแนะนำให้อ่านหน้า กฎและแพลตฟอร์ม ก่อนดำเนินการต่อ หน้านี้จะอธิบายเหตุผลที่ต้องใช้ Toolchain วิธีการกำหนดและใช้งาน Toolchain รวมถึงวิธีที่ Bazel เลือก Toolchain ที่เหมาะสมตามข้อจำกัดของแพลตฟอร์ม

แรงจูงใจ

ก่อนอื่นเรามาดูปัญหาที่ Toolchain ได้รับการออกแบบมาเพื่อแก้ไข สมมติว่าคุณกำลังเขียนกฎเพื่อรองรับภาษาโปรแกรม "bar" กฎ bar_binary จะคอมไพล์ไฟล์ *.bar โดยใช้คอมไพเลอร์ barc ซึ่งเป็นเครื่องมือที่สร้างขึ้นเป็นเป้าหมายอื่นในพื้นที่ทำงาน เนื่องจากผู้ใช้ที่เขียนเป้าหมาย bar_binary ไม่ควรต้องระบุทรัพยากร Dependency กับคอมไพเลอร์ คุณจึงทำให้คอมไพเลอร์เป็นทรัพยากร Dependency โดยนัยด้วยการเพิ่มลงในการกำหนดกฎเป็นแอตทริบิวต์ส่วนตัว

bar_binary = rule(
    implementation = _bar_binary_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = True),
        ...
        "_compiler": attr.label(
            default = "//bar_tools:barc_linux",  # the compiler running on linux
            providers = [BarcInfo],
        ),
    },
)

ตอนนี้ //bar_tools:barc_linux เป็นทรัพยากร Dependency ของเป้าหมาย bar_binary ทุกรายการ ดังนั้นระบบจะสร้าง //bar_tools:barc_linux ก่อนเป้าหมาย bar_binary ฟังก์ชันการใช้งานของกฎจะเข้าถึง `//bar_tools:barc_linux` ได้เหมือนกับแอตทริบิวต์อื่นๆ ดังนี้

BarcInfo = provider(
    doc = "Information about how to invoke the barc compiler.",
    # In the real world, compiler_path and system_lib might hold File objects,
    # but for simplicity they are strings for this example. arch_flags is a list
    # of strings.
    fields = ["compiler_path", "system_lib", "arch_flags"],
)

def _bar_binary_impl(ctx):
    ...
    info = ctx.attr._compiler[BarcInfo]
    command = "%s -l %s %s" % (
        info.compiler_path,
        info.system_lib,
        " ".join(info.arch_flags),
    )
    ...

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

โซลูชันที่ไม่เหมาะสมคือการผลักภาระให้ผู้ใช้โดยทำให้แอตทริบิวต์ _compiler ไม่เป็นส่วนตัว จากนั้นเป้าหมายแต่ละรายการจะฮาร์ดโค้ดเพื่อสร้างสำหรับแพลตฟอร์มหนึ่งหรืออีกแพลตฟอร์มหนึ่งได้

bar_binary(
    name = "myprog_on_linux",
    srcs = ["mysrc.bar"],
    compiler = "//bar_tools:barc_linux",
)

bar_binary(
    name = "myprog_on_windows",
    srcs = ["mysrc.bar"],
    compiler = "//bar_tools:barc_windows",
)

คุณปรับปรุงโซลูชันนี้ได้โดยใช้ select เพื่อเลือก compiler ตามแพลตฟอร์ม ดังนี้

config_setting(
    name = "on_linux",
    constraint_values = [
        "@platforms//os:linux",
    ],
)

config_setting(
    name = "on_windows",
    constraint_values = [
        "@platforms//os:windows",
    ],
)

bar_binary(
    name = "myprog",
    srcs = ["mysrc.bar"],
    compiler = select({
        ":on_linux": "//bar_tools:barc_linux",
        ":on_windows": "//bar_tools:barc_windows",
    }),
)

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

เฟรมเวิร์ก Toolchain แก้ปัญหานี้โดยเพิ่มระดับการอ้างอิงทางอ้อม โดยพื้นฐานแล้ว คุณจะประกาศว่ากฎมีการขึ้นอยู่แบบนามธรรมกับสมาชิก บางราย ของกลุ่มเป้าหมาย (ประเภท Toolchain) และ Bazel จะแก้ปัญหานี้โดยอัตโนมัติเป็นเป้าหมายที่เฉพาะเจาะจง (Toolchain) ตามข้อจำกัดของแพลตฟอร์มที่เกี่ยวข้อง ทั้งผู้เขียนกฎและผู้เขียนเป้าหมายไม่จำเป็นต้องทราบชุดแพลตฟอร์มและ Toolchain ที่มีทั้งหมด

การเขียนกฎที่ใช้ Toolchain

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

# By convention, toolchain_type targets are named "toolchain_type" and
# distinguished by their package path. So the full path for this would be
# //bar_tools:toolchain_type.
toolchain_type(name = "toolchain_type")

การกำหนดกฎในส่วนก่อนหน้าจะได้รับการแก้ไขเพื่อให้กฎประกาศว่าใช้ Toolchain //bar_tools:toolchain_type แทนที่จะรับคอมไพเลอร์เป็นแอตทริบิวต์

bar_binary = rule(
    implementation = _bar_binary_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = True),
        ...
        # No `_compiler` attribute anymore.
    },
    toolchains = ["//bar_tools:toolchain_type"],
)

ตอนนี้ฟังก์ชันการใช้งานจะเข้าถึงการขึ้นอยู่ดังกล่าวภายใต้ ctx.toolchains แทน ctx.attr โดยใช้ประเภท Toolchain เป็นคีย์

def _bar_binary_impl(ctx):
    ...
    info = ctx.toolchains["//bar_tools:toolchain_type"].barcinfo
    # The rest is unchanged.
    command = "%s -l %s %s" % (
        info.compiler_path,
        info.system_lib,
        " ".join(info.arch_flags),
    )
    ...

ctx.toolchains["//bar_tools:toolchain_type"] จะแสดง ToolchainInfo ผู้ให้บริการ ของเป้าหมายใดก็ตามที่ Bazel แก้ปัญหาทรัพยากร Dependency ของ Toolchain กฎของเครื่องมือพื้นฐานจะเป็นผู้กำหนดช่องของออบเจ็กต์ ToolchainInfo ในส่วนถัดไป เราจะกำหนดกฎนี้เพื่อให้มีช่อง barcinfo ที่ครอบคลุมออบเจ็กต์ BarcInfo

ขั้นตอนของ Bazel ในการแก้ปัญหา Toolchain เป็นเป้าหมายมีดังนี้ ด้านล่าง เฉพาะเป้าหมาย Toolchain ที่แก้ปัญหาแล้วเท่านั้นที่จะกลายเป็นการขึ้นอยู่ของเป้าหมาย bar_binary ไม่ใช่พื้นที่ทั้งหมดของ Toolchain ที่เป็นไปได้

Toolchain ที่บังคับและไม่บังคับ

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

คุณประกาศการขึ้นอยู่ของประเภท Toolchain แบบไม่บังคับ ได้ดังนี้

bar_binary = rule(
    ...
    toolchains = [
        config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False),
    ],
)

เมื่อแก้ปัญหาประเภท Toolchain แบบไม่บังคับไม่ได้ ระบบจะดำเนินการวิเคราะห์ต่อ และผลลัพธ์ของ ctx.toolchains["//bar_tools:toolchain_type"] คือ None

ฟังก์ชัน config_common.toolchain_type จะตั้งค่าเริ่มต้นเป็นบังคับ

คุณใช้รูปแบบต่อไปนี้ได้

  • ประเภท Toolchain ที่บังคับ
    • toolchains = ["//bar_tools:toolchain_type"]
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type")]
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = True)]
  • ประเภท Toolchain แบบไม่บังคับ
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False)]
bar_binary = rule(
    ...
    toolchains = [
        "//foo_tools:toolchain_type",
        config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False),
    ],
)

คุณยังสามารถผสมและจับคู่รูปแบบในกฎเดียวกันได้ด้วย อย่างไรก็ตาม หากมีการระบุประเภท Toolchain เดียวกันหลายครั้ง ระบบจะใช้เวอร์ชันที่เข้มงวดที่สุด โดยที่บังคับจะเข้มงวดกว่าไม่บังคับ

การเขียนแง่มุมที่ใช้ Toolchain

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

bar_aspect = aspect(
    implementation = _bar_aspect_impl,
    attrs = {},
    toolchains = ['//bar_tools:toolchain_type'],
)

def _bar_aspect_impl(target, ctx):
  toolchain = ctx.toolchains['//bar_tools:toolchain_type']
  # Use the toolchain provider like in a rule.
  return []

การกำหนด Toolchain

หากต้องการกำหนด Toolchain บางรายการสำหรับประเภท Toolchain ที่กำหนด คุณต้องมีสิ่งต่อไปนี้ 3 อย่าง

  1. กฎเฉพาะภาษาที่แสดงถึงเครื่องมือหรือชุดเครื่องมือ โดยธรรมเนียมแล้ว ชื่อกฎนี้จะมีคำต่อท้ายเป็น "_toolchain"

    1. หมายเหตุ: กฎ \_toolchain ไม่สามารถสร้างการดำเนินการบิลด์ใดๆ ได้ แต่จะรวบรวมอาร์ติแฟกต์จากกฎอื่นๆ และส่งต่ออาร์ติแฟกต์เหล่านั้นไปยังกฎที่ใช้ Toolchain กฎดังกล่าวมีหน้าที่รับผิดชอบในการสร้างการดำเนินการสร้างทั้งหมด
  2. เป้าหมายหลายรายการของกฎประเภทนี้ ซึ่งแสดงถึงเครื่องมือหรือชุดเครื่องมือเวอร์ชันต่างๆ สำหรับแพลตฟอร์มต่างๆ

  3. สำหรับเป้าหมายแต่ละรายการดังกล่าว เป้าหมายที่เชื่อมโยงของกฎทั่วไป toolchain จะให้ข้อมูลเมตาที่เฟรมเวิร์ก Toolchain ใช้ เป้าหมาย toolchain นี้ยังอ้างอิงถึง toolchain_type ที่เชื่อมโยงกับ Toolchain นี้ด้วย ซึ่งหมายความว่ากฎ _toolchain ที่กำหนดอาจเชื่อมโยงกับ toolchain_type ใดก็ได้ และเฉพาะในอินสแตนซ์ toolchain ที่ใช้ กฎ _toolchain นี้เท่านั้นที่กฎจะเชื่อมโยงกับ toolchain_type

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

def _bar_toolchain_impl(ctx):
    toolchain_info = platform_common.ToolchainInfo(
        barcinfo = BarcInfo(
            compiler_path = ctx.attr.compiler_path,
            system_lib = ctx.attr.system_lib,
            arch_flags = ctx.attr.arch_flags,
        ),
    )
    return [toolchain_info]

bar_toolchain = rule(
    implementation = _bar_toolchain_impl,
    attrs = {
        "compiler_path": attr.string(),
        "system_lib": attr.string(),
        "arch_flags": attr.string_list(),
    },
)

กฎต้องแสดงผู้ให้บริการ ToolchainInfo ซึ่งจะกลายเป็นออบเจ็กต์ที่กฎที่ใช้ดึงข้อมูลโดยใช้ ctx.toolchains และป้ายกำกับของประเภท Toolchain ToolchainInfo สามารถเก็บคู่คีย์-ค่าที่กำหนดเองได้เหมือนกับ struct ข้อกำหนดของช่องที่เพิ่มลงใน ToolchainInfo ควรมีการระบุไว้อย่างชัดเจนที่ประเภท Toolchain ในตัวอย่างนี้ ค่าจะแสดงผลโดยครอบคลุมในออบเจ็กต์ BarcInfo เพื่อนำสคีมาที่กำหนดไว้ข้างต้นกลับมาใช้ใหม่ สไตล์นี้อาจมีประโยชน์สำหรับการตรวจสอบและการนำโค้ดกลับมาใช้ใหม่

ตอนนี้คุณกำหนดเป้าหมายสำหรับคอมไพเลอร์ barc ที่เฉพาะเจาะจงได้แล้ว

bar_toolchain(
    name = "barc_linux",
    arch_flags = [
        "--arch=Linux",
        "--debug_everything",
    ],
    compiler_path = "/path/to/barc/on/linux",
    system_lib = "/usr/lib/libbarc.so",
)

bar_toolchain(
    name = "barc_windows",
    arch_flags = [
        "--arch=Windows",
        # Different flags, no debug support on windows.
    ],
    compiler_path = "C:\\path\\on\\windows\\barc.exe",
    system_lib = "C:\\path\\on\\windows\\barclib.dll",
)

สุดท้าย ให้สร้างการกำหนด toolchain สำหรับเป้าหมาย bar_toolchain 2 รายการ การกำหนดเหล่านี้จะลิงก์เป้าหมายเฉพาะภาษาเข้ากับประเภท Toolchain และให้ข้อมูลข้อจำกัดที่บอก Bazel ว่า Toolchain เหมาะสมกับแพลตฟอร์มที่กำหนดเมื่อใด

toolchain(
    name = "barc_linux_toolchain",
    exec_compatible_with = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
    ],
    target_compatible_with = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
    ],
    toolchain = ":barc_linux",
    toolchain_type = ":toolchain_type",
)

toolchain(
    name = "barc_windows_toolchain",
    exec_compatible_with = [
        "@platforms//os:windows",
        "@platforms//cpu:x86_64",
    ],
    target_compatible_with = [
        "@platforms//os:windows",
        "@platforms//cpu:x86_64",
    ],
    toolchain = ":barc_windows",
    toolchain_type = ":toolchain_type",
)

การใช้ไวยากรณ์พาธสัมพัทธ์ด้านบนแสดงให้เห็นว่าการกำหนดเหล่านี้อยู่ในแพ็กเกจเดียวกัน แต่ไม่มีเหตุผลใดที่ประเภท Toolchain, เป้าหมาย Toolchain เฉพาะภาษา และเป้าหมายการกำหนด toolchain จะอยู่ในแพ็กเกจแยกกันไม่ได้

ดูตัวอย่างการใช้งานจริงได้ที่ go_toolchain

Toolchain และการกำหนดค่า

คำถามสำคัญสำหรับผู้เขียนกฎคือ เมื่อมีการวิเคราะห์เป้าหมาย bar_toolchain เป้าหมายจะเห็นการกำหนดค่าใด และควรใช้การเปลี่ยนผ่านใดสำหรับการขึ้นอยู่ ตัวอย่างด้านบนใช้แอตทริบิวต์สตริง แต่จะเกิดอะไรขึ้นกับ Toolchain ที่ซับซ้อนมากขึ้นซึ่งขึ้นอยู่กับเป้าหมายอื่นๆ ในที่เก็บ Bazel

มาดู bar_toolchain เวอร์ชันที่ซับซ้อนมากขึ้นกัน

def _bar_toolchain_impl(ctx):
    # The implementation is mostly the same as above, so skipping.
    pass

bar_toolchain = rule(
    implementation = _bar_toolchain_impl,
    attrs = {
        "compiler": attr.label(
            executable = True,
            mandatory = True,
            cfg = "exec",
        ),
        "system_lib": attr.label(
            mandatory = True,
            cfg = "target",
        ),
        "arch_flags": attr.string_list(),
    },
)

การใช้ attr.label จะเหมือนกับกฎมาตรฐาน แต่ความหมายของพารามิเตอร์ cfg จะแตกต่างกันเล็กน้อย

ทรัพยากร Dependency จากเป้าหมาย (เรียกว่า "รายการหลัก") ไปยัง Toolchain ผ่านการแก้ปัญหา Toolchain จะใช้การเปลี่ยนผ่านการกำหนดค่าพิเศษที่เรียกว่า "การเปลี่ยนผ่าน Toolchain" การเปลี่ยนผ่าน Toolchain จะรักษาการกำหนดค่าไว้เหมือนเดิม ยกเว้นว่าจะบังคับให้แพลตฟอร์มการดำเนินการเหมือนกันสำหรับ Toolchain และรายการหลัก (มิฉะนั้น การแก้ปัญหา Toolchain สำหรับ Toolchain อาจเลือกแพลตฟอร์มการดำเนินการใดก็ได้ และไม่จำเป็นต้องเหมือนกับรายการหลัก) การดำเนินการนี้ช่วยให้ทรัพยากร Dependency exec ของ Toolchain สามารถปฏิบัติการได้สำหรับการดำเนินการสร้างของรายการหลักด้วย การขึ้นอยู่ของ Toolchain ใดก็ตามที่ใช้ cfg = "target" (หรือไม่ได้ระบุ cfg เนื่องจาก "target" เป็นค่าเริ่มต้น) จะ สร้างขึ้นสำหรับแพลตฟอร์มเป้าหมายเดียวกันกับรายการหลัก การดำเนินการนี้ช่วยให้กฎ Toolchain สามารถมีส่วนร่วมทั้งไลบรารี (แอตทริบิวต์ system_lib ด้านบน) และเครื่องมือ (แอตทริบิวต์ compiler) ในกฎการสร้างที่จำเป็น ระบบจะลิงก์ไลบรารีของระบบเข้ากับอาร์ติแฟกต์สุดท้าย ดังนั้นจึงต้องสร้างขึ้นสำหรับแพลตฟอร์มเดียวกัน ในขณะที่คอมไพเลอร์เป็นเครื่องมือที่เรียกใช้ระหว่างการบิลด์ และต้องสามารถทำงานบนแพลตฟอร์มการดำเนินการได้

การลงทะเบียนและการสร้างด้วย Toolchain

ตอนนี้เราได้ประกอบองค์ประกอบที่ใช้สร้างสรรค์ทั้งหมดเข้าด้วยกันแล้ว และคุณเพียงแค่ต้องทำให้ Toolchain พร้อมใช้งานสำหรับขั้นตอนการแก้ปัญหาของ Bazel โดยทำได้ด้วยการลงทะเบียน Toolchain ในไฟล์ MODULE.bazel โดยใช้ register_toolchains() หรือส่งป้ายกำกับของ Toolchain ในบรรทัดคำสั่งโดยใช้แฟล็ก --extra_toolchains

register_toolchains(
    "//bar_tools:barc_linux_toolchain",
    "//bar_tools:barc_windows_toolchain",
    # Target patterns are also permitted, so you could have also written:
    # "//bar_tools:all",
    # or even
    # "//bar_tools/...",
)

เมื่อใช้รูปแบบเป้าหมายเพื่อลงทะเบียน Toolchain ลำดับการลงทะเบียน Toolchain แต่ละรายการจะกำหนดโดยกฎต่อไปนี้

  • ระบบจะลงทะเบียน Toolchain ที่กำหนดไว้ในแพ็กเกจย่อยของแพ็กเกจก่อน Toolchain ที่กำหนดไว้ในแพ็กเกจเอง
  • ภายในแพ็กเกจ ระบบจะลงทะเบียน Toolchain ตามลำดับตัวอักษรของชื่อ

ตอนนี้เมื่อคุณสร้างเป้าหมายที่ขึ้นอยู่กับประเภท Toolchain ระบบจะเลือก Toolchain ที่เหมาะสมตามแพลตฟอร์มเป้าหมายและแพลตฟอร์มการดำเนินการ

# my_pkg/BUILD

platform(
    name = "my_target_platform",
    constraint_values = [
        "@platforms//os:linux",
    ],
)

bar_binary(
    name = "my_bar_binary",
    ...
)
bazel build //my_pkg:my_bar_binary --platforms=//my_pkg:my_target_platform

Bazel จะเห็นว่าระบบกำลังสร้าง //my_pkg:my_bar_binary ด้วยแพลตฟอร์มที่มี @platforms//os:linux ดังนั้นจึงแก้ปัญหาการอ้างอิง //bar_tools:toolchain_type เป็น //bar_tools:barc_linux_toolchain ซึ่งจะส่งผลให้ระบบสร้าง //bar_tools:barc_linux แต่ไม่ใช่ //bar_tools:barc_windows

การแก้ปัญหา Toolchain

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

ระบบจะรวบรวมแพลตฟอร์มการดำเนินการและ Toolchain ที่พร้อมใช้งานจาก กราฟทรัพยากร Dependency ภายนอกผ่าน register_execution_platforms และ register_toolchains การเรียกใน MODULE.bazel ไฟล์ นอกจากนี้ คุณยังระบุแพลตฟอร์มการดำเนินการและ Toolchain เพิ่มเติมใน บรรทัดคำสั่งผ่าน --extra_execution_platforms และ --extra_toolchainsได้ด้วย ระบบจะรวมแพลตฟอร์มโฮสต์เป็นแพลตฟอร์มการดำเนินการที่พร้อมใช้งานโดยอัตโนมัติ ระบบจะติดตามแพลตฟอร์มและ Toolchain ที่พร้อมใช้งานเป็นรายการที่เรียงลำดับเพื่อความแน่นอน โดยจะให้ความสำคัญกับรายการก่อนหน้าในรายการ

ระบบจะสร้างชุด Toolchain ที่พร้อมใช้งานตามลำดับความสำคัญจาก --extra_toolchains และ register_toolchains ดังนี้

  1. ระบบจะเพิ่ม Toolchain ที่ลงทะเบียนโดยใช้ --extra_toolchains ก่อน (Toolchain สุดท้าย ใน Toolchain เหล่านี้มีความสำคัญสูงสุด)
  2. Toolchain ที่ลงทะเบียนโดยใช้ register_toolchains ในกราฟทรัพยากร Dependency ภายนอกแบบถ่ายทอดตามลำดับต่อไปนี้ (Toolchain แรก ที่กล่าวถึงใน Toolchain เหล่านี้มีความสำคัญสูงสุด)
    1. Toolchain ที่ลงทะเบียนโดยโมดูลราก (เช่น MODULE.bazel ที่รากพื้นที่ทำงาน)
    2. Toolchain ที่ลงทะเบียนในไฟล์ WORKSPACE ของผู้ใช้ รวมถึงในมาโครใดก็ตามที่เรียกใช้จากไฟล์ดังกล่าว
    3. Toolchain ที่ลงทะเบียนโดยโมดูลที่ไม่ใช่ราก (เช่น การขึ้นอยู่ที่ระบุโดยโมดูลราก และการขึ้นอยู่ของโมดูลเหล่านั้น เป็นต้น)
    4. Toolchain ที่ลงทะเบียนใน "คำต่อท้าย WORKSPACE" ซึ่งใช้โดยกฎเนทีฟบางรายการที่รวมอยู่ในการติดตั้ง Bazel เท่านั้น

หมายเหตุ: ระบบจะจัดลำดับเป้าหมายเสมือน เช่น :all, :* และ /... ตามกลไกการโหลดแพ็กเกจ ของ Bazel ซึ่งใช้การจัดลำดับตามตัวอักษร

ขั้นตอนการแก้ปัญหามีดังนี้

  1. อนุประโยค target_compatible_with หรือ exec_compatible_with จะ ตรงกัน กับแพลตฟอร์มหากแพลตฟอร์มมี constraint_value เดียวกันกับ constraint_value แต่ละรายการในรายการ (ไม่ว่าจะระบุอย่างชัดเจนหรือเป็นค่าเริ่มต้น)

    หากแพลตฟอร์มมี constraint_value จาก constraint_setting ที่อนุประโยคไม่ได้อ้างอิง การดำเนินการนี้จะไม่ส่งผลต่อการจับคู่

  2. หากเป้าหมายที่กำลังสร้างระบุแ101exec_compatible_withตริบิวต์ (หรือการกำหนดกฎระบุอ101exec_compatible_withกิวเมนต์)101ระบบจะกรองรายการแพลตฟอร์มการดำเนินการที่พร้อมใช้งานเพื่อนำ101แพลตฟอร์มที่ไม่ตรงกับข้อจำกัดการดำเนินการออก

  3. ระบบจะกรองรายการ Toolchain ที่พร้อมใช้งานเพื่อนำ Toolchain ที่ระบุ target_settings ซึ่งไม่ตรงกับการกำหนดค่าปัจจุบันออก

  4. สำหรับแพลตฟอร์มการดำเนินการที่พร้อมใช้งานแต่ละแพลตฟอร์ม ให้เชื่อมโยงประเภท Toolchain แต่ละประเภทกับ Toolchain แรกที่พร้อมใช้งาน (หากมี) ซึ่งเข้ากันได้กับแพลตฟอร์มการดำเนินการและแพลตฟอร์มเป้าหมาย

  5. ระบบจะตัดแพลตฟอร์มการดำเนินการที่ไม่พบ Toolchain ที่บังคับที่เข้ากันได้สำหรับประเภท Toolchain อย่างน้อย 1 ประเภทออก จากแพลตฟอร์มที่เหลือ แพลตฟอร์มแรกจะกลายเป็นแพลตฟอร์มการดำเนินการของเป้าหมายปัจจุบัน และ Toolchain ที่เชื่อมโยง (หากมี) จะกลายเป็นทรัพยากร Dependency ของเป้าหมาย

ระบบจะใช้แพลตฟอร์มการดำเนินการที่เลือกเพื่อเรียกใช้การดำเนินการทั้งหมดที่เป้าหมายสร้างขึ้น

ในกรณีที่สร้างเป้าหมายเดียวกันได้ในการกำหนดค่าหลายรายการ (เช่น สำหรับ CPU ต่างๆ) ภายในบิลด์เดียวกัน ระบบจะใช้ขั้นตอนการแก้ปัญหาแยกกันกับเป้าหมายแต่ละเวอร์ชัน

หากกฎใช้กลุ่มการดำเนินการ กลุ่มการดำเนินการแต่ละกลุ่มจะทำการแก้ปัญหา Toolchain แยกกัน และแต่ละกลุ่มจะมีแพลตฟอร์มการดำเนินการและ Toolchain ของตัวเอง

การแก้ไขข้อบกพร่องของ Toolchain

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

ตัวอย่างเช่น หากต้องการแก้ไขข้อบกพร่องการเลือก Toolchain สำหรับการดำเนินการทั้งหมดที่สร้างขึ้นโดยตรงโดย //my:target ให้ใช้คำสั่งต่อไปนี้

$ bazel build //my:all --toolchain_resolution_debug=//my:target

หากต้องการแก้ไขข้อบกพร่องการเลือก Toolchain สำหรับการดำเนินการทั้งหมดในเป้าหมายการสร้างทั้งหมด ให้ใช้คำสั่งต่อไปนี้

$ bazel build //my:all --toolchain_resolution_debug=.*

หากต้องการดูว่าทรัพยากร Dependency ของ cquery รายการใดมาจากการแก้ปัญหา Toolchain ให้ใช้ cquery's --transitions ดังนี้

# Find all direct dependencies of //cc:my_cc_lib. This includes explicitly
# declared dependencies, implicit dependencies, and toolchain dependencies.
$ bazel cquery 'deps(//cc:my_cc_lib, 1)'
//cc:my_cc_lib (96d6638)
@bazel_tools//tools/cpp:toolchain (96d6638)
@bazel_tools//tools/def_parser:def_parser (HOST)
//cc:my_cc_dep (96d6638)
@local_config_platform//:host (96d6638)
@bazel_tools//tools/cpp:toolchain_type (96d6638)
//:default_host_platform (96d6638)
@local_config_cc//:cc-compiler-k8 (HOST)
//cc:my_cc_lib.cc (null)
@bazel_tools//tools/cpp:grep-includes (HOST)

# Which of these are from toolchain resolution?
$ bazel cquery 'deps(//cc:my_cc_lib, 1)' --transitions=lite | grep "toolchain dependency"
  [toolchain dependency]#@local_config_cc//:cc-compiler-k8#HostTransition -> b6df211