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

รายงานปัญหา ดูแหล่งที่มา Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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

แรงจูงใจ

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

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 เป็นทรัพยากรที่ต้องใช้ของเป้าหมาย bar_binary ทุกรายการ ดังนั้นระบบจะสร้าง //bar_tools:barc_linux ก่อนเป้าหมาย 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 แทน ประเภท 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")

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

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

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

เครื่องมือที่ต้องใช้และไม่บังคับ

โดยค่าเริ่มต้น เมื่อกฎแสดงการอ้างอิงประเภท 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 จะเป็นฟังก์ชันที่ต้องระบุโดยค่าเริ่มต้น

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

  • ประเภทเครื่องมือที่จำเป็น
    • 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),
    ],
)

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

การเขียนที่ใช้เครื่องมือ

Aspect มีสิทธิ์เข้าถึง API ของ Toolchain เดียวกันกับกฎ โดยคุณสามารถกำหนดประเภท 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 ที่ต้องการ คุณต้องมี 3 สิ่งต่อไปนี้

  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 provider ซึ่งจะกลายเป็นออบเจ็กต์ที่กฎการใช้งานดึงข้อมูลโดยใช้ ctx.toolchains และป้ายกำกับของประเภทเครื่องมือ ToolchainInfo เช่น struct สามารถเก็บคู่ฟิลด์-ค่าที่กำหนดเองได้ การระบุฟิลด์ที่เพิ่มลงใน ToolchainInfo ควรมีการบันทึกอย่างชัดเจนที่ประเภทเครื่องมือ ในตัวอย่างนี้ ค่าจะแสดงในออบเจ็กต์ 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 ตัวอย่างข้างต้นใช้แอตทริบิวต์สตริง แต่จะเกิดอะไรขึ้นกับเครื่องมือที่ซับซ้อนกว่าซึ่งขึ้นอยู่กับเป้าหมายอื่นๆ ในที่เก็บ 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 จะแตกต่างกันเล็กน้อย

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

เครื่องมือแก้ไขข้อบกพร่อง

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

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

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

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

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

หากต้องการดูว่าการขึ้นต่อกันของ 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