Chuỗi công cụ

Báo cáo sự cố Xem nguồn

Trang này mô tả khung chuỗi công cụ. Đây là cách để người viết quy tắc giải mã logic quy tắc của họ bằng việc lựa chọn công cụ dựa trên nền tảng. Bạn nên đọc các trang về quy tắcnề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 hạn chế của nền tảng.

Động lực

Trước tiên, hãy cùng tìm hiểu chuỗi công cụ giải quyết vấn đề. 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 của bạn sẽ biên dịch các tệp *.bar bằng trình biên dịch barc. Đây là một công cụ tự đượ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 các mục tiêu bar_binary không phải chỉ định một phần phụ thuộc trên trình biên dịch, nên bạn sẽ biến phần phụ thuộc đó 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 một 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 bất kỳ mục tiêu bar_binary nào. Hàm triển khai của quy tắc có thể truy cập thuộc tính này 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 xây dựng – được gọi là nền tảng mục tiêunền tảng thực thi, tương ứng. Hơn nữa, tác giả quy tắc thậm chí không nhất thiết phải biết tất cả các công cụ và nền tảng có sẵn, vì vậy, không thể mã hoá cứng chúng trong định nghĩa của quy tắc.

Một giải pháp ít lý tưởng hơn là chuyển gánh nặng cho người dùng bằng cách đặt thuộc tính _compiler ở chế độ không riêng tư. Sau đó, các mục tiêu riêng lẻ có thể được mã hoá cứng để xây dựng 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",
    }),
)

Tuy nhiên, việc này khá tẻ nhạt và có một chút yêu cầu đối với mọi người dùng bar_binary. Nếu kiểu này không được sử dụng nhất quán trên toàn bộ không gian làm việc, thì điều này sẽ dẫn đến việc các bản dựng hoạt động hiệu quả trên một nền tảng nhưng sẽ không thành công khi mở rộng sang các trường hợp đa nền tảng. Phiên bản này cũng không giải quyết 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 cần sửa đổi các quy tắc hoặc mục tiêu hiện có.

Khung chuỗi công cụ sẽ 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ó phần phụ thuộc trừu tượng vào một số thành viên của nhóm mục tiêu (loại chuỗi công cụ) và Bazel sẽ tự động giải quyết vấn đề này thành một mục tiêu cụ thể (chuỗi công cụ) dựa trên các quy tắc hạn chế hiện hành của nền tảng. Cả tác giả quy tắc và tác giả mục tiêu đều không cần phải biết tập hợp hoàn chỉnh các nền tảng và chuỗi công cụ có sẵn.

Viết những quy tắc sử dụng chuỗi công cụ

Trong khung chuỗi công cụ, thay vì có các quy tắc phụ thuộc trực tiếp vào công cụ, thì 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ụ đóng 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, hãy khai báo rằng 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 vào 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ề nhà cung cấp ToolchainInfo của bất kỳ mục tiêu nào mà Bazel đã giải quyết 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 gói đối tượng BarcInfo.

Quy trình phân giải chuỗi công cụ cho các mục tiêu của Bazel được mô tả dưới đây. Chỉ mục tiêu chuỗi công cụ đã đượ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ụ ứng viên.

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 cách sử dụng một nhãn trống (như trình bày ở trên), thì loại chuỗi công cụ được coi là bắt buộc. Nếu Bazel không tìm thấy chuỗi công cụ phù hợp (xem phần Độ phân giải chuỗi công cụ dưới đây) để biết loại chuỗi công cụ bắt buộc, thì đây là một lỗi và quá trình phân tích sẽ tạm dừng.

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"]None.

Theo mặc định, hàm config_common.toolchain_type 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 và so khớ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ì phiên bản nghiêm ngặt nhất sẽ được áp dụng, trong đó tính bắt buộc nghiêm ngặt hơn phiên bản không bắt buộc.

Các khía cạnh viết có 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ụ dưới dạng 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 chuỗi công cụ thông qua ngữ cảnh và sử dụng chúng để 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 thực hiện 3 điều sau:

  1. Một quy tắc theo ngôn ngữ cụ thể đạ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 thêm hậu tố "_toolchain".

    1. Lưu ý: Quy tắc \_toolchain không thể tạo bất kỳ hành động liên quan đến bản dựng nào. Thay vào đó, phương thức này thu thập 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 hành động xây dựng.
  2. 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 các nền tảng khác nhau.

  3. Đố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 chung toolchain, để cung cấp siêu dữ liệu mà khung chuỗi công cụ sử dụng. Mục tiêu toolchain này cũng tham chiếu đến toolchain_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 thực thể toolchain sử dụng quy tắc _toolchain này mới liên kết với một toolchain_type.

Trong ví dụ về chạy của chúng ta, 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 này phải trả về một trình cung cấp ToolchainInfo. Quy tắc này sẽ trở thành đối tượng mà quy tắc sử dụng truy xuất bằng cách sử dụ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ỳ ý. Thông số kỹ thuật chính xác về các trường được thêm vào ToolchainInfo phải được ghi lại rõ ràng theo 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 đồ đã 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 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 đị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 theo ngôn ngữ cụ thể 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 những đị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ụ theo ngôn ngữ cụ thể và các 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 các tác giả quy tắc là khi phân tích một mục tiêu bar_toolchain, mục tiêu đó sẽ thấy cấu hình nào và chuyển đổi nào sẽ được sử dụng 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 phiên bản phức tạp hơn của 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(),
    },
)

Việc sử dụng attr.label giống như đối với quy tắc tiêu chuẩn, nhưng ý nghĩa của tham số cfg hơi khác.

Phần phụ thuộc từ mục tiêu (gọi là "mẹ") đến 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ó tên là "chuyển đổi chuỗi công cụ". Quá trình chuyển đổi chuỗi công cụ giúp giữ nguyên cấu hình, ngoại trừ việc chuyển đổi này buộc nền tảng thực thi của chuỗi công cụ phải giống nhau đối với chuỗi công cụ (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 như nền tảng thực thi gốc). Đ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 cho các hành động tạo 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 phần phụ thuộc không chỉ định cfg, vì "mục tiêu" là giá trị mặc định) đều được xây dựng cho cùng một nền tảng đích như thành phần 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 xây dựng cần đến chúng. Các thư viện hệ thống được liên kết với cấu phần phần mềm cuối cùng nên 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 tạo và cần chạy được trên nền tảng thực thi.

Đăng ký và xây dựng bằng chuỗi công cụ

Đến đây, tất cả các thành phần đã được tập hợp lại và bạn chỉ cần cung cấp các chuỗi công cụ cho quy trình giải quyết 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 MODULE.bazel bằng register_toolchains() hoặc bằng cách chuyể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",
    # or even
    # "//bar_tools/...",
)

Khi sử dụng mẫu mục tiêu để đăng ký chuỗi công cụ, thứ tự đăng ký các chuỗi công cụ riêng lẻ được xác định theo các quy tắc sau:

  • Các chuỗi công cụ được xác định trong một gói con của một gói được đăng ký trước các chuỗi công cụ được xác định trong chính gói đó.
  • Trong một gói, các chuỗi công cụ được đăng ký theo thứ tự từ vựng của tên.

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 nền tảng mục tiêu và 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 xây dựng bằng một nền tảng có @platforms//os:linux, do đó giải quyết tham chiếu //bar_tools:toolchain_type đến //bar_tools:barc_linux_toolchain. Thao tác này sẽ kết thúc việc tạo //bar_tools:barc_linux chứ không phải //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 chuỗi công cụ cụ thể của mục tiêu. Quy trình này lấy thông tin đầu vào là một tập hợp 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 hiện có và danh sách các chuỗi công cụ hiện có. Đầu ra của ứng dụng là một chuỗi công cụ được chọn cho từng 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ụ có sẵn được thu thập từ biểu đồ phần phụ thuộc bên ngoài thông qua các lệnh gọi register_execution_platformsregister_toolchains trong tệp MODULE.bazel. Bạn cũng có thể chỉ định các nền tảng thực thi và chuỗi công cụ khác trên dòng lệnh thông qua --extra_execution_platforms--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ó thể sử dụng. 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 theo thứ tự cho thuật toán xác định, với lựa chọn ưu tiên cho các mục trước đó trong danh sách.

Một tập hợp chuỗi công cụ có sẵn (theo thứ tự ưu tiên) được tạo từ --extra_toolchainsregister_toolchains:

  1. Các chuỗi công cụ được đăng ký bằng --extra_toolchains sẽ được thêm trước tiên. (Trong các danh mục này, chuỗi công cụ cuối cùng có mức độ ưu tiên cao nhất.)
  2. Các chuỗi công cụ được đăng ký bằng register_toolchains trong biểu đồ phần phụ thuộc chuyển tiếp bên ngoài theo thứ tự sau: (Trong các chuỗi công cụ này, chuỗi công cụ đầu tiên đề cập có mức độ ưu tiên cao nhất.)
    1. Các chuỗi công cụ được đăng ký bởi mô-đun gốc (như trong MODULE.bazel tại thư mục gốc của không gian làm việc);
    2. Các chuỗi công cụ được đăng ký trong tệp WORKSPACE của người dùng, bao gồm cả trong mọi macro được gọi từ đó;
    3. Chuỗi công cụ được đăng ký bởi các mô-đun không phải gốc (như trong các phần phụ thuộc do mô-đun gốc chỉ định và các phần phụ thuộc của các mô-đun đó, v.v.);
    4. Các chuỗi công cụ được đăng ký trong "Hậu tố WORKSPACE". Hậu tố này chỉ được sử dụng trong một số quy tắc gốc nhất định đi kèm với bản cài đặt Bazel.

LƯU Ý: Các mục tiêu giả như :all, :*/... được sắp xếp theo cơ chế tải gói của Bazel, sử dụng thứ tự từ vựng.

Các bước giải quyết như sau.

  1. Mệnh đề target_compatible_with hoặc exec_compatible_with khớp với một nền tảng nếu, đối với mỗi constraint_value trong danh sách, nền tảng cũng có constraint_value đó (một cách rõ ràng hoặc mặc định).

    Nếu nền tảng có các constraint_value từ constraint_setting không được tham chiếu bởi mệnh đề, thì các giá trị này sẽ không ảnh hưởng đến việc so khớp.

  2. 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 thuộc tính đó 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.

  3. Danh sách chuỗi công cụ có sẵn được lọc để xoá mọi chuỗi công cụ chỉ định target_settings không khớp với cấu hình hiện tại.

  4. Đối với mỗi nền tảng thực thi có sẵn, bạn sẽ 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.

  5. Hệ thống sẽ loại trừ mọi nền tảng thực thi 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 đó. Trong các nền tảng còn lại, nền tảng đầu tiên 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ể xây dựng 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 giải quyết sẽ được áp dụng một cách độ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ụ một cách 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.

Chuỗi công cụ gỡ lỗi

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ờ 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ẽ đưa ra tên của các chuỗi công cụ mà công cụ này kiểm tra và bỏ qua trong quá trình phân giải.

Nếu bạn muốn biết phần phụ thuộc cquery nào đến từ quá trình 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