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

รายงานปัญหา ดูแหล่งที่มา

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

แรงจูงใจ

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

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) และ Bazel แก้ไขปัญหานี้กับเป้าหมายเฉพาะ (เชนเครื่องมือ) โดยอัตโนมัติตามข้อจำกัดของแพลตฟอร์มที่เกี่ยวข้อง ทั้งผู้เขียนกฎและผู้เขียนเป้าหมายไม่จำเป็นต้องรู้ชุดแพลตฟอร์มและห่วงโซ่เครื่องมือที่มีอยู่ทั้งหมด

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

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

# 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")

มีการแก้ไขคำจำกัดความกฎในส่วนก่อนหน้านี้เพื่อให้แทนที่คอมไพเลอร์เป็นแอตทริบิวต์ เพื่อใช้เชนเครื่องมือ //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"],
)

ตอนนี้ฟังก์ชันการใช้งานจะเข้าถึงทรัพยากร Dependency นี้ใน 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 แก้ไขการพึ่งพาเครื่องมือ ช่องของออบเจ็กต์ ToolchainInfo จะกำหนดโดยกฎของเครื่องมือที่สำคัญ ในส่วนถัดไป กฎนี้ได้รับการกำหนดไว้ว่ามีช่อง barcinfo ที่รวมออบเจ็กต์ BarcInfo

ขั้นตอนการแก้ไขห่วงโซ่เครื่องมือให้กับเป้าหมายของ Bazel ได้อธิบายไว้ด้านล่าง เฉพาะเป้าหมาย Toolchain ที่แก้ไขแล้วเท่านั้นที่ถือว่าเป็นทรัพยากร Dependency ของเป้าหมาย bar_binary ไม่ใช่พื้นที่ทั้งหมดของ Toolchain ของผู้สมัคร

เชนเครื่องมือที่จำเป็นและที่ไม่บังคับ

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

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

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

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

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

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

  • ประเภทห่วงโซ่เครื่องมือที่จำเป็นมีดังนี้
    • 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 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 []

การกำหนดห่วงโซ่เครื่องมือ

หากต้องการกำหนดห่วงโซ่เครื่องมือสำหรับประเภทเชนเครื่องมือหนึ่งๆ คุณต้องมี 3 สิ่งต่อไปนี้

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

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

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

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

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 สำหรับตัวอย่างการใช้งานจริง

เชนเครื่องมือและการกำหนดค่า

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

การลงทะเบียนและสร้างด้วย 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 ที่เหมาะสมตามแพลตฟอร์มเป้าหมายและการดำเนินการ

# 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

การแก้ปัญหาเชนเครื่องมือ

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

ระบบจะรวบรวมแพลตฟอร์มการดำเนินการและ Toolchain ที่พร้อมใช้งานจากกราฟทรัพยากร Dependency ภายนอกผ่านการเรียกใช้ register_execution_platforms และ register_toolchains ใน MODULE.bazelfiles. Additional execution platforms and toolchains may also be specified on the command line via [--extra_execution_platforms](/reference/command-line-reference#flag--extra_execution_platforms) and [--extra_toolchains`](/reference/command-line-reference#flag--extra_toolchains) แพลตฟอร์มโฮสต์จะรวมอยู่ในแพลตฟอร์มการดำเนินการที่ใช้งานได้โดยอัตโนมัติ แพลตฟอร์มและชุดเครื่องมือที่ใช้ได้จะได้รับการติดตามเป็นลำดับรายการสำหรับการตัดสินใจกำหนด โดยให้ความสำคัญกับรายการก่อนหน้าในรายการ

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

หากต้องการดูว่าทรัพยากร Dependency ของ cquery ใดมาจาก Toolchain Resolution ให้ใช้แฟล็ก --transitions ของ cquery ดังนี้

# 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