หน้านี้จะอธิบายถึงเฟรมเวิร์ก 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 เป็นเป้าหมายที่เรียบง่าย ซึ่งแสดงถึงคลาสของเครื่องมือที่มีบทบาทเดียวกันสำหรับแพลตฟอร์มต่างๆ ตัวอย่างเช่น คุณประกาศประเภทที่แสดงถึงแท่งคอมไพเลอร์ได้
# 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 แก้ไขทรัพยากร Dependency ของ Toolchain ช่องของออบเจ็กต์ ToolchainInfo
จะกำหนดโดยกฎของเครื่องมือที่สำคัญ ในส่วนถัดไป กฎนี้มีการกำหนดไว้ว่ามีช่อง barcinfo
ที่รวมออบเจ็กต์ BarcInfo
ขั้นตอนการแก้ปัญหาของ Bazel สำหรับการแก้ไขปัญหาชุดเครื่องมือเป็นเป้าหมายได้อธิบายไว้ด้านล่าง เฉพาะเป้าหมาย Toolchain ที่ได้รับการแก้ไขเท่านั้นที่เป็นทรัพยากร Dependency ของเป้าหมาย bar_binary
ไม่ใช่พื้นที่ทั้งหมดของ Toolchain ของผู้สมัคร
เชนเครื่องมือที่จำเป็นและที่ไม่บังคับ
โดยค่าเริ่มต้น เมื่อกฎแสดงทรัพยากร Dependency ของประเภท Toolchain โดยใช้ป้ายกำกับเปล่า (ดังที่แสดงด้านบน) จะถือว่าประเภท Toolchain เป็นบังคับ หาก Bazel ไม่พบห่วงโซ่เครื่องมือที่ตรงกัน (ดูการแก้ปัญหาของ 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)]
- ประเภทห่วงโซ่เครื่องมือ (ไม่บังคับ)
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 หนึ่งๆ คุณต้องมี 3 สิ่งต่อไปนี้
กฎเฉพาะภาษาซึ่งแสดงประเภทเครื่องมือหรือชุดเครื่องมือ โดยทั่วไปแล้ว ชื่อของกฎนี้ต่อท้ายด้วย "_toolchain"
- หมายเหตุ: กฎ
\_toolchain
ไม่สามารถสร้างการดำเนินการของบิลด์ใดๆ ได้ แต่จะรวบรวมอาร์ติแฟกต์จากกฎอื่นๆ และส่งต่อไปยังกฎที่ใช้ Toolchain กฎนั้นมีหน้าที่สร้าง การดำเนินการของบิลด์ทั้งหมด
- หมายเหตุ: กฎ
เป้าหมายหลายรายการของกฎประเภทนี้ โดยเป็นตัวแทนของเวอร์ชันของเครื่องมือหรือชุดเครื่องมือสำหรับแพลตฟอร์มต่างๆ
สำหรับแต่ละเป้าหมายดังกล่าว ซึ่งเป็นเป้าหมายที่เกี่ยวข้องของกฎ
toolchain
ทั่วไปเพื่อระบุข้อมูลเมตาที่ใช้โดยเฟรมเวิร์ก Toolchain เป้าหมายtoolchain
นี้หมายถึงtoolchain_type
ที่เชื่อมโยงกับ Toolchain นี้ด้วยเช่นกัน ซึ่งหมายความว่ากฎ_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 รายการ
คำจำกัดความเหล่านี้จะลิงก์เป้าหมายเฉพาะภาษากับประเภทเชนเครื่องมือและให้ข้อมูลข้อจำกัดที่แจ้งให้ 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 สามารถเลือกแพลตฟอร์มการดำเนินการได้และอาจไม่เหมือนกับเวอร์ชันหลัก) ซึ่งจะทำให้ทรัพยากร Dependency ของ exec
ของ Toolchain เรียกใช้การดำเนินการของบิลด์ระดับบนสุดได้ด้วย ทรัพยากร Dependency ของ Toolchain ที่ใช้ cfg =
"target"
(หรือไม่ได้ระบุ cfg
เนื่องจาก "target" เป็นค่าเริ่มต้น) สร้างขึ้นสำหรับแพลตฟอร์มเป้าหมายเดียวกันกับแพลตฟอร์มหลัก ซึ่งจะช่วยให้กฎ Toolchain มีทั้งไลบรารี (แอตทริบิวต์ system_lib
ด้านบน) และเครื่องมือ (แอตทริบิวต์ compiler
) ให้กับกฎบิลด์ที่ต้องใช้ ไลบรารีระบบจะลิงก์กับอาร์ติแฟกต์สุดท้าย และจำเป็นต้องสร้างขึ้นสำหรับแพลตฟอร์มเดียวกัน ในขณะที่คอมไพเลอร์เป็นเครื่องมือที่เรียกใช้ระหว่างบิลด์และจำเป็นต้องเรียกใช้บนแพลตฟอร์มการดำเนินการ
การลงทะเบียนและสร้างด้วย Toolchain
ณ จุดนี้ระบบจะประกอบองค์ประกอบพื้นฐานทั้งหมด และคุณเพียงแค่ต้องทำให้เชนเครื่องมือพร้อมใช้งานในขั้นตอนการแก้ปัญหาของ Bazel ซึ่งทำได้ด้วยการลงทะเบียน Toolchain ทั้งในไฟล์ MODULE.bazel
โดยใช้ 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",
# or even
# "//bar_tools/...",
)
เมื่อใช้รูปแบบเป้าหมายในการลงทะเบียนห่วงโซ่เครื่องมือ ลำดับการลงทะเบียนเชนเครื่องมือแต่ละรายการจะกำหนดโดยกฎต่อไปนี้
- เชนเครื่องมือที่กำหนดไว้ในแพ็กเกจย่อยของแพ็กเกจจะได้รับการลงทะเบียนก่อนเครื่องมือเชนที่กำหนดไว้ในแพ็กเกจ
- ภายในแพ็กเกจ เชนเครื่องมือจะได้รับการบันทึกตามลำดับพจนานุกรมของชื่อ
ในตอนนี้เมื่อคุณสร้างเป้าหมายที่ขึ้นอยู่กับประเภท 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
สำหรับแต่ละเป้าหมายที่ใช้ห่วงโซ่เครื่องมือ ขั้นตอนการแก้ปัญหาชุดเครื่องมือของ Bazel จะกำหนดทรัพยากร Dependency ของห่วงโซ่เครื่องมือที่เป็นรูปธรรมของเป้าหมาย กระบวนการนี้จะป้อนชุดประเภท Toolchain ที่จำเป็น แพลตฟอร์มเป้าหมาย รายการแพลตฟอร์มการดำเนินการที่ใช้ได้ และรายการ Toolchain ที่มีอยู่ เอาต์พุตของเครื่องมือนี้คือห่วงโซ่เครื่องมือที่เลือกสำหรับ Toolchain แต่ละประเภท รวมถึงแพลตฟอร์มการดำเนินการที่เลือกสำหรับเป้าหมายปัจจุบัน
แพลตฟอร์มการดำเนินการและเชนเครื่องมือที่พร้อมใช้งานจะรวบรวมจากกราฟการอ้างอิงภายนอกผ่าน
register_execution_platforms
และ
register_toolchains
การเรียกใช้ในไฟล์
MODULE.bazel
อาจระบุแพลตฟอร์มการดำเนินการและเชนเครื่องมือเพิ่มเติมในบรรทัดคำสั่งผ่าน --extra_execution_platforms
และ --extra_toolchains
ได้ด้วย
แพลตฟอร์มโฮสต์จะรวมอยู่ในแพลตฟอร์มการดำเนินการที่ใช้งานได้โดยอัตโนมัติ
แพลตฟอร์มและห่วงโซ่เครื่องมือที่ใช้ได้จะมีการติดตามเป็นรายการตามลำดับสำหรับการตัดสินใจเชิงกำหนด โดยให้ความสำคัญกับรายการก่อนหน้าในรายการ
ชุดของเชนเครื่องมือที่ใช้ได้จะสร้างขึ้นจาก --extra_toolchains
และ register_toolchains
ตามลำดับความสำคัญ
- มีการเพิ่มเชนเครื่องมือที่ลงทะเบียนโดยใช้
--extra_toolchains
ก่อน (ภายในตัวเลือกเหล่านี้ เชนเครื่องมือสุดท้ายมีลำดับความสำคัญสูงสุด) - เชนเครื่องมือที่ลงทะเบียนโดยใช้
register_toolchains
ในกราฟการอ้างอิงภายนอกแบบทรานซิทีฟตามลำดับต่อไปนี้ (ในเชนเครื่องมือที่พูดถึงจากรายการแรกจะมีลำดับความสำคัญสูงสุด)- เชนเครื่องมือที่ลงทะเบียนโดยโมดูลรูท (เช่น
MODULE.bazel
ที่รูทของพื้นที่ทำงาน) - เชนเครื่องมือที่ลงทะเบียนไว้ในไฟล์
WORKSPACE
ของผู้ใช้ รวมถึงในมาโครที่เรียกใช้จากที่นั่น - เชนเครื่องมือที่ลงทะเบียนโดยโมดูลที่ไม่ใช่รูท (เช่น ทรัพยากร Dependency ที่ระบุโดยโมดูลรูท และทรัพยากร Dependency ของเชน ฯลฯ)
- เชนเครื่องมือที่ลงทะเบียนไว้ใน "คำต่อท้าย WORKSPACE" ซึ่งจะใช้โดยกฎเนทีฟบางข้อที่รวมกับการติดตั้ง Bazel เท่านั้น
- เชนเครื่องมือที่ลงทะเบียนโดยโมดูลรูท (เช่น
หมายเหตุ: เป้าหมายที่ไม่ระบุตัวบุคคล เช่น :all
, :*
และ /...
เรียงลำดับตามกลไกการโหลดแพ็กเกจของ Bazel ซึ่งใช้ลำดับแบบพจนานุกรม
ขั้นตอนการแก้ปัญหามีดังนี้
วรรค
target_compatible_with
หรือexec_compatible_with
จะตรงกับแพลตฟอร์มก็ต่อเมื่อสำหรับconstraint_value
แต่ละรายการในรายการ แพลตฟอร์มมีconstraint_value
นั้นเช่นกัน (โดยชัดแจ้งหรือเป็นค่าเริ่มต้น)หากแพลตฟอร์มมี
constraint_value
จากconstraint_setting
ที่ไม่ได้อ้างอิงโดยอนุประโยค ค่าเหล่านี้จะไม่ส่งผลต่อการจับคู่หากเป้าหมายที่สร้างระบุแอตทริบิวต์
exec_compatible_with
(หรือคำจำกัดความของกฎระบุอาร์กิวเมนต์exec_compatible_with
) ระบบจะกรองรายการแพลตฟอร์มการดำเนินการที่ใช้ได้เพื่อนำรายการที่ไม่ตรงกับข้อจำกัดการดำเนินการออกระบบจะกรองรายการเชนเครื่องมือที่ใช้ได้เพื่อนำเชนเครื่องมือที่ระบุ
target_settings
ออก ซึ่งไม่ตรงกับการกำหนดค่าปัจจุบันสำหรับแพลตฟอร์มการดำเนินการที่ใช้ได้แต่ละแพลตฟอร์ม คุณต้องเชื่อมโยง Toolchain แต่ละประเภทกับ Toolchain แรกที่พร้อมใช้งาน (หากมี) ซึ่งเข้ากันได้กับแพลตฟอร์มการดำเนินการนี้และแพลตฟอร์มเป้าหมาย (หากมี)
โดยจะตัดแพลตฟอร์มการดำเนินการใดๆ ที่ไม่พบเครื่องมือที่จำเป็นซึ่งใช้งานร่วมกันได้กับประเภท Toolchain จากแพลตฟอร์มที่เหลือ แพลตฟอร์มแรกจะกลายเป็นแพลตฟอร์มการดำเนินการของเป้าหมายปัจจุบัน และ Toolchain ที่เกี่ยวข้อง (หากมี) จะกลายเป็นทรัพยากร Dependency ของเป้าหมาย
ระบบจะใช้แพลตฟอร์มการดำเนินการที่เลือกเพื่อเรียกใช้การดำเนินการทั้งหมดที่เป้าหมายสร้างขึ้น
ในกรณีที่สร้างเป้าหมายเดียวกันได้ในการกำหนดค่าหลายรายการ (เช่น สำหรับ CPU ที่ต่างกัน) ภายในบิลด์เดียวกัน ระบบจะใช้ขั้นตอนการแก้ปัญหาโดยอิสระกับเป้าหมายแต่ละเวอร์ชัน
หากกฎใช้กลุ่มการดำเนินการ กลุ่มการดำเนินการแต่ละกลุ่มจะดำเนินการแก้ปัญหา Toolchain แยกกัน และแต่ละกลุ่มจะมีแพลตฟอร์มการดำเนินการและ Toolchain ของตนเอง
การแก้ไขข้อบกพร่องห่วงโซ่เครื่องมือ
หากคุณจะเพิ่มการสนับสนุนเชนเครื่องมือลงในกฎที่มีอยู่ ให้ใช้แฟล็ก --toolchain_resolution_debug=regex
ในระหว่างการแก้ไข Toolchain แฟล็กจะแสดงผลแบบละเอียดสำหรับประเภท Toolchain หรือชื่อเป้าหมายที่ตรงกับตัวแปรนิพจน์ทั่วไป คุณสามารถใช้ .*
เพื่อแสดงข้อมูลทั้งหมด Bazel จะแสดงชื่อของห่วงโซ่เครื่องมือที่ตรวจสอบและข้ามในขั้นตอนการแก้ปัญหา
หากต้องการดูทรัพยากร Dependency ของ cquery
ที่มาจากการแก้ไขเครื่องมือเชน ให้ใช้แฟล็ก --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