Trang này mô tả khung chuỗi công cụ, đây là cách để tác giả quy tắc tách rời logic quy tắc khỏi lựa chọn công cụ dựa trên nền tảng. Bạn nên đọc trang quy tắc và nền tảng trước khi tiếp tục. Trang này trình bày lý do cần có chuỗi công cụ, cách xác định và sử dụng chuỗi công cụ cũng như cách Bazel chọn một chuỗi công cụ phù hợp dựa trên các quy tắc ràng buộc của nền tảng.
Động lực
Trước tiên, hãy xem vấn đề mà chuỗi công cụ được thiết kế để giải quyết. Giả sử bạn đang viết các quy tắc để hỗ trợ ngôn ngữ lập trình "bar". Quy tắc bar_binary
sẽ biên dịch các tệp *.bar
bằng trình biên dịch barc
, một công cụ được tạo dưới dạng một mục tiêu khác trong không gian làm việc của bạn. Vì người dùng viết mục tiêu bar_binary
không cần phải chỉ định phần phụ thuộc trên trình biên dịch, nên bạn sẽ đặt phần phụ thuộc này thành phần phụ thuộc ngầm ẩn bằng cách thêm phần phụ thuộc đó vào định nghĩa quy tắc dưới dạng thuộc tính riêng tư.
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
hiện là phần phụ thuộc của mọi mục tiêu bar_binary
, vì vậy, mục tiêu này sẽ được tạo trước mọi mục tiêu bar_binary
. Bạn có thể truy cập vào thuộc tính này bằng hàm triển khai của quy tắc giống như mọi thuộc tính khác:
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),
)
...
Vấn đề ở đây là nhãn của trình biên dịch được mã hoá cứng vào bar_binary
, tuy nhiên, các mục tiêu khác nhau có thể cần các trình biên dịch khác nhau tuỳ thuộc vào nền tảng mà chúng đang được tạo và nền tảng mà chúng đang được tạo – được gọi là nền tảng mục tiêu và nền tảng thực thi tương ứng. Hơn nữa, tác giả quy tắc không nhất thiết phải biết tất cả các công cụ và nền tảng hiện có, vì vậy, không thể mã hoá cứng các công cụ và nền tảng đó trong định nghĩa của quy tắc.
Một giải pháp không lý tưởng là chuyển gánh nặng cho người dùng bằng cách đặt thuộc tính _compiler
thành không riêng tư. Sau đó, bạn có thể mã hoá cứng từng mục tiêu để tạo cho một nền tảng hoặc nền tảng khác.
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",
)
Bạn có thể cải thiện giải pháp này bằng cách sử dụng select
để chọn compiler
dựa trên nền tảng:
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",
}),
)
Nhưng việc này khá tẻ nhạt và hơi quá đáng đối với mỗi người dùng bar_binary
.
Nếu bạn không sử dụng nhất quán kiểu này trong không gian làm việc, thì các bản dựng sẽ hoạt động tốt trên một nền tảng nhưng không hoạt động khi mở rộng sang các tình huống nhiều nền tảng. Phương pháp này cũng không giải quyết được vấn đề thêm tính năng hỗ trợ cho các nền tảng và trình biên dịch mới mà không sửa đổi các quy tắc hoặc mục tiêu hiện có.
Khung chuỗi công cụ giải quyết vấn đề này bằng cách thêm một cấp độ gián tiếp bổ sung. Về cơ bản, bạn khai báo rằng quy tắc của mình có một phần phụ thuộc trừu tượng trên một số thành viên của một nhóm mục tiêu (loại chuỗi công cụ) và Bazel sẽ tự động phân giải phần phụ thuộc này thành một mục tiêu cụ thể (một chuỗi công cụ) dựa trên các quy tắc ràng buộc nền tảng hiện hành. Cả tác giả quy tắc và tác giả mục tiêu đều không cần biết bộ nền tảng và chuỗi công cụ có sẵn.
Viết quy tắc sử dụng chuỗi công cụ
Trong khung chuỗi công cụ, thay vì các quy tắc phụ thuộc trực tiếp vào công cụ, các quy tắc này phụ thuộc vào các loại chuỗi công cụ. Loại chuỗi công cụ là một mục tiêu đơn giản đại diện cho một lớp công cụ có cùng vai trò cho nhiều nền tảng. Ví dụ: bạn có thể khai báo một loại đại diện cho trình biên dịch thanh:
# 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")
Định nghĩa quy tắc trong phần trước được sửa đổi để thay vì lấy trình biên dịch làm thuộc tính, quy tắc này sẽ khai báo rằng quy tắc này sử dụng chuỗi công cụ //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"],
)
Hàm triển khai hiện truy cập phần phụ thuộc này trong ctx.toolchains
thay vì ctx.attr
, sử dụng loại chuỗi công cụ làm khoá.
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"]
trả về trình cung cấp ToolchainInfo
của bất kỳ mục tiêu nào mà Bazel đã phân giải phần phụ thuộc chuỗi công cụ. Các trường của đối tượng ToolchainInfo
được đặt theo quy tắc của công cụ cơ bản; trong phần tiếp theo, quy tắc này được xác định sao cho có một trường barcinfo
bao bọc đối tượng BarcInfo
.
Quy trình của Bazel để phân giải chuỗi công cụ thành các mục tiêu được mô tả dưới đây. Chỉ mục tiêu chuỗi công cụ đã phân giải mới thực sự được tạo thành phần phụ thuộc của mục tiêu bar_binary
, chứ không phải toàn bộ không gian của chuỗi công cụ đề xuất.
Chuỗi công cụ bắt buộc và không bắt buộc
Theo mặc định, khi một quy tắc thể hiện phần phụ thuộc loại chuỗi công cụ bằng nhãn trần (như minh hoạ ở trên), loại chuỗi công cụ được coi là bắt buộc. Nếu Bazel không tìm thấy một chuỗi công cụ phù hợp (xem Độ phân giải chuỗi công cụ bên dưới) cho một loại chuỗi công cụ bắt buộc, thì đây là lỗi và quá trình phân tích sẽ dừng lại.
Thay vào đó, bạn có thể khai báo phần phụ thuộc loại chuỗi công cụ không bắt buộc như sau:
bar_binary = rule(
...
toolchains = [
config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False),
],
)
Khi không thể phân giải một loại chuỗi công cụ không bắt buộc, quá trình phân tích sẽ tiếp tục và kết quả của ctx.toolchains[""//bar_tools:toolchain_type"]
là None
.
Hàm config_common.toolchain_type
mặc định là bắt buộc.
Bạn có thể sử dụng các biểu mẫu sau:
- Các loại chuỗi công cụ bắt buộc:
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)]
- Các loại chuỗi công cụ không bắt buộc:
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),
],
)
Bạn cũng có thể kết hợp các biểu mẫu trong cùng một quy tắc. Tuy nhiên, nếu cùng một loại chuỗi công cụ được liệt kê nhiều lần, thì hệ thống sẽ sử dụng phiên bản nghiêm ngặt nhất, trong đó phần bắt buộc nghiêm ngặt hơn phần không bắt buộc.
Các khía cạnh viết sử dụng chuỗi công cụ
Các khía cạnh có quyền truy cập vào cùng một API chuỗi công cụ như các quy tắc: bạn có thể xác định các loại chuỗi công cụ bắt buộc, truy cập vào chuỗi công cụ thông qua ngữ cảnh và sử dụng các chuỗi công cụ đó để tạo các thao tác mới bằng chuỗi công cụ.
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 []
Xác định chuỗi công cụ
Để xác định một số chuỗi công cụ cho một loại chuỗi công cụ nhất định, bạn cần có 3 yếu tố:
Quy tắc dành riêng cho ngôn ngữ đại diện cho loại công cụ hoặc bộ công cụ. Theo quy ước, tên của quy tắc này có hậu tố là "_toolchain".
- Lưu ý: Quy tắc
\_toolchain
không thể tạo bất kỳ hành động bản dựng nào. Thay vào đó, quy tắc này thu thập các cấu phần phần mềm từ các quy tắc khác và chuyển tiếp các cấu phần phần mềm đó đến quy tắc sử dụng chuỗi công cụ. Quy tắc đó chịu trách nhiệm tạo tất cả các thao tác bản dựng.
- Lưu ý: Quy tắc
Một số mục tiêu của loại quy tắc này, đại diện cho các phiên bản của công cụ hoặc bộ công cụ cho nhiều nền tảng.
Đối với mỗi mục tiêu như vậy, một mục tiêu liên kết của quy tắc
toolchain
chung, để cung cấp siêu dữ liệu mà khung chuỗi công cụ sử dụng. Mục tiêutoolchain
này cũng tham chiếu đếntoolchain_type
liên kết với chuỗi công cụ này. Điều này có nghĩa là một quy tắc_toolchain
nhất định có thể được liên kết với bất kỳtoolchain_type
nào và chỉ trong một thực thểtoolchain
sử dụng quy tắc_toolchain
này thì quy tắc đó mới được liên kết vớitoolchain_type
.
Đối với ví dụ đang chạy, sau đây là định nghĩa cho quy tắc bar_toolchain
. Ví dụ của chúng ta chỉ có một trình biên dịch, nhưng các công cụ khác như trình liên kết cũng có thể được nhóm bên dưới trình biên dịch.
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(),
},
)
Quy tắc phải trả về một trình cung cấp ToolchainInfo
. Trình cung cấp này sẽ trở thành đối tượng mà quy tắc sử dụng truy xuất bằng ctx.toolchains
và nhãn của loại chuỗi công cụ. ToolchainInfo
, giống như struct
, có thể chứa các cặp giá trị trường tuỳ ý. Bạn phải ghi lại rõ ràng thông số kỹ thuật về những trường được thêm vào ToolchainInfo
tại loại chuỗi công cụ. Trong ví dụ này, các giá trị trả về được gói trong đối tượng BarcInfo
để sử dụng lại giản đồ được xác định ở trên; kiểu này có thể hữu ích cho việc xác thực và sử dụng lại mã.
Giờ đây, bạn có thể xác định các mục tiêu cho các trình biên dịch barc
cụ thể.
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",
)
Cuối cùng, bạn tạo các định nghĩa toolchain
cho hai mục tiêu bar_toolchain
.
Các định nghĩa này liên kết các mục tiêu dành riêng cho ngôn ngữ với loại chuỗi công cụ và cung cấp thông tin ràng buộc cho Bazel biết thời điểm chuỗi công cụ phù hợp với một nền tảng nhất định.
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",
)
Việc sử dụng cú pháp đường dẫn tương đối ở trên cho thấy rằng tất cả các định nghĩa này đều nằm trong cùng một gói, nhưng không có lý do gì khiến loại chuỗi công cụ, mục tiêu chuỗi công cụ dành riêng cho ngôn ngữ và mục tiêu định nghĩa toolchain
không thể nằm trong các gói riêng biệt.
Hãy xem go_toolchain
để biết ví dụ thực tế.
Chuỗi công cụ và cấu hình
Một câu hỏi quan trọng đối với tác giả quy tắc là khi phân tích mục tiêu bar_toolchain
, mục tiêu đó sẽ thấy cấu hình nào và nên sử dụng hiệu ứng chuyển đổi nào cho các phần phụ thuộc? Ví dụ trên sử dụng các thuộc tính chuỗi, nhưng điều gì sẽ xảy ra với một chuỗi công cụ phức tạp hơn phụ thuộc vào các mục tiêu khác trong kho lưu trữ Bazel?
Hãy xem một phiên bản bar_toolchain
phức tạp hơn:
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(),
},
)
Cách sử dụng attr.label
giống như cách sử dụng quy tắc tiêu chuẩn, nhưng ý nghĩa của tham số cfg
có chút khác biệt.
Phần phụ thuộc từ một mục tiêu (được gọi là "mục tiêu mẹ") đến một chuỗi công cụ thông qua độ phân giải chuỗi công cụ sử dụng một quá trình chuyển đổi cấu hình đặc biệt được gọi là "quá trình chuyển đổi chuỗi công cụ". Quá trình chuyển đổi chuỗi công cụ giữ nguyên cấu hình, ngoại trừ việc buộc nền tảng thực thi phải giống nhau cho chuỗi công cụ như đối với chuỗi công cụ mẹ (nếu không, độ phân giải chuỗi công cụ cho chuỗi công cụ có thể chọn bất kỳ nền tảng thực thi nào và không nhất thiết phải giống với nền tảng thực thi mẹ). Điều này cho phép mọi phần phụ thuộc exec
của chuỗi công cụ cũng có thể thực thi được cho các thao tác bản dựng của thành phần mẹ. Mọi phần phụ thuộc của chuỗi công cụ sử dụng cfg =
"target"
(hoặc không chỉ định cfg
, vì "target" là mặc định) đều được tạo cho cùng một nền tảng mục tiêu với phần phụ thuộc mẹ. Điều này cho phép các quy tắc chuỗi công cụ đóng góp cả thư viện (thuộc tính system_lib
ở trên) và công cụ (thuộc tính compiler
) cho các quy tắc bản dựng cần đến chúng. Thư viện hệ thống được liên kết vào cấu phần phần mềm cuối cùng và do đó cần được tạo cho cùng một nền tảng, trong khi trình biên dịch là một công cụ được gọi trong quá trình xây dựng và cần có thể chạy trên nền tảng thực thi.
Đăng ký và tạo bằng chuỗi công cụ
Tại thời điểm này, tất cả các khối xây dựng đều được tập hợp và bạn chỉ cần cung cấp
chuỗi công cụ cho quy trình phân giải của Bazel. Bạn có thể thực hiện việc này bằng cách đăng ký chuỗi công cụ trong tệp WORKSPACE
bằng register_toolchains()
hoặc truyền nhãn của chuỗi công cụ trên dòng lệnh bằng cờ --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",
)
Giờ đây, khi bạn tạo một mục tiêu phụ thuộc vào loại chuỗi công cụ, một chuỗi công cụ thích hợp sẽ được chọn dựa trên mục tiêu và nền tảng thực thi.
# 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 sẽ thấy //my_pkg:my_bar_binary
đang được tạo bằng một nền tảng có @platforms//os:linux
, do đó, hãy phân giải tham chiếu //bar_tools:toolchain_type
đến //bar_tools:barc_linux_toolchain
.
Thao tác này sẽ tạo //bar_tools:barc_linux
nhưng không tạo //bar_tools:barc_windows
.
Độ phân giải của chuỗi công cụ
Đối với mỗi mục tiêu sử dụng chuỗi công cụ, quy trình phân giải chuỗi công cụ của Bazel sẽ xác định các phần phụ thuộc cụ thể của chuỗi công cụ của mục tiêu. Quy trình này lấy một nhóm các loại chuỗi công cụ bắt buộc, nền tảng mục tiêu, danh sách các nền tảng thực thi có sẵn và danh sách các chuỗi công cụ có sẵn làm dữ liệu đầu vào. Đầu ra của công cụ này là một chuỗi công cụ đã chọn cho mỗi loại chuỗi công cụ cũng như một nền tảng thực thi đã chọn cho mục tiêu hiện tại.
Các nền tảng thực thi và chuỗi công cụ hiện có được thu thập từ tệp WORKSPACE
thông qua register_execution_platforms
và register_toolchains
.
Bạn cũng có thể chỉ định các nền tảng thực thi và chuỗi công cụ bổ sung trên dòng lệnh thông qua --extra_execution_platforms
và --extra_toolchains
.
Nền tảng lưu trữ sẽ tự động được đưa vào dưới dạng nền tảng thực thi có sẵn.
Các nền tảng và chuỗi công cụ có sẵn được theo dõi dưới dạng danh sách đã sắp xếp để xác định tính chất cố định, trong đó các mục trước đó trong danh sách được ưu tiên.
Sau đây là các bước giải quyết.
Mệnh đề
target_compatible_with
hoặcexec_compatible_with
khớp với một nền tảng nếu đối với mỗiconstraint_value
trong danh sách, nền tảng đó cũng cóconstraint_value
đó (một cách rõ ràng hoặc theo mặc định).Nếu nền tảng có
constraint_value
từconstraint_setting
không được tham chiếu bằng mệnh đề, thì cácconstraint_value
này sẽ không ảnh hưởng đến việc so khớp.Nếu mục tiêu đang được tạo chỉ định thuộc tính
exec_compatible_with
(hoặc định nghĩa quy tắc của mục tiêu chỉ định đối sốexec_compatible_with
), thì danh sách các nền tảng thực thi có sẵn sẽ được lọc để xoá mọi nền tảng không khớp với các quy tắc ràng buộc thực thi.Đối với mỗi nền tảng thực thi có sẵn, bạn liên kết từng loại chuỗi công cụ với chuỗi công cụ đầu tiên có sẵn (nếu có) tương thích với nền tảng thực thi này và nền tảng mục tiêu.
Bất kỳ nền tảng thực thi nào không tìm thấy chuỗi công cụ bắt buộc tương thích cho một trong các loại chuỗi công cụ của nền tảng đó đều bị loại trừ. Trong số các nền tảng còn lại, nền tảng đầu tiên sẽ trở thành nền tảng thực thi của mục tiêu hiện tại và các chuỗi công cụ liên kết (nếu có) sẽ trở thành phần phụ thuộc của mục tiêu.
Nền tảng thực thi đã chọn được dùng để chạy tất cả các hành động mà mục tiêu tạo ra.
Trong trường hợp có thể tạo cùng một mục tiêu trong nhiều cấu hình (chẳng hạn như cho nhiều CPU) trong cùng một bản dựng, quy trình phân giải sẽ được áp dụng độc lập cho từng phiên bản của mục tiêu.
Nếu quy tắc sử dụng nhóm thực thi, thì mỗi nhóm thực thi sẽ thực hiện việc phân giải chuỗi công cụ riêng biệt và mỗi nhóm có nền tảng thực thi và chuỗi công cụ riêng.
Gỡ lỗi chuỗi công cụ
Nếu bạn đang thêm tính năng hỗ trợ chuỗi công cụ vào một quy tắc hiện có, hãy sử dụng cờ --toolchain_resolution_debug=regex
. Trong quá trình phân giải chuỗi công cụ, cờ này cung cấp kết quả chi tiết cho các loại chuỗi công cụ hoặc tên mục tiêu khớp với biến biểu thức chính quy. Bạn có thể sử dụng .*
để xuất tất cả thông tin. Bazel sẽ xuất ra tên của các chuỗi công cụ mà nó kiểm tra và bỏ qua trong quá trình phân giải.
Nếu bạn muốn xem phần phụ thuộc cquery
nào là từ độ phân giải chuỗi công cụ, hãy sử dụng cờ --transitions
của 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