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

รายงานปัญหา ดูแหล่งที่มา /3} /4} {3/4} {3/4} {3/4} {3/4} /4.

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

เฟรมเวิร์กเครื่องมือเชนจะแก้ปัญหานี้ด้วยการเพิ่มการเปลี่ยนเส้นทางทางอ้อมอีกระดับ โดยพื้นฐานแล้ว คุณประกาศว่ากฎมีทรัพยากร Dependency แบบนามธรรมกับสมาชิกบางคนในกลุ่มเป้าหมาย (ประเภท 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")

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

ตอนนี้ฟังก์ชันการใช้งานจะเข้าถึงทรัพยากร 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 เป้าหมายได้แก้ไขทรัพยากร Dependency ของ Toolchain ช่องของออบเจ็กต์ 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),
    ],
)

เมื่อแก้ไขประเภท 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

Aspect มีสิทธิ์เข้าถึง 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

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

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

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

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

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

เมื่อนำองค์ประกอบทั้งหมดมาประกอบกันแล้ว คุณเพียงแค่ต้องทำให้ Toolchain พร้อมใช้งานสำหรับขั้นตอนการแก้ปัญหาของ Bazel ซึ่งทำได้โดยลงทะเบียน Toolchain ในไฟล์ WORKSPACE โดยใช้ register_toolchains() หรือส่งป้ายกำกับของ Toolchains ในบรรทัดคำสั่งโดยใช้แฟล็ก --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",
)

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

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

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

  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 แรกที่มี (หากมี) ที่เข้ากันได้กับแพลตฟอร์มการดําเนินการนี้และแพลตฟอร์มเป้าหมาย

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

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

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

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

การแก้ไขข้อบกพร่องเกี่ยวกับโซ่เครื่องมือ

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

หากต้องการดูว่าทรัพยากร Dependency ของ cquery ใดมาจากความละเอียดของ Toolchain ให้ใช้แฟล็ก --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