도구 모음

<ph type="x-smartling-placeholder"></ph> 문제 신고 <ph type="x-smartling-placeholder"></ph> 소스 보기 1박 · 7.3 · 7.2 · 7.1 · 7.0 · 6.5 를 참조하세요.

이 페이지에서는 규칙 작성자가 도구 모음 프레임워크에 대해 플랫폼 기반 도구 선택에서 규칙 로직을 분리할 수 있습니다. 그것은 규칙플랫폼 페이지를 방문하세요. 이 페이지에서는 도구 모음이 필요한 이유와 Bazel이 적합한 도구 모음을 선택하는 방법과 이를 기반으로 제약 조건을 덜어줍니다

동기

먼저 도구 모음이 해결하도록 설계된 문제를 살펴보겠습니다. 예를 들어 'bar'를 지지하는 규칙을 작성하고 있으며 프로그래밍 언어라고 할 수 있습니다. 내 bar_binary 규칙은 자체를 실행하는 도구인 barc 컴파일러를 사용하여 *.bar 파일을 컴파일합니다. 작업공간에서 또 다른 대상으로 빌드됩니다 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_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 사용자에게 요청해야 할 수도 있습니다. 이 스타일이 작업공간 전체에서 일관되게 사용되지 않으면 단일 플랫폼에서는 잘 작동하지만 다음으로 확장 시 실패하는 빌드 멀티 플랫폼 시나리오를 지원합니다 또한 지원 추가와 관련된 문제도 해결되지 않습니다. 을 사용하는 것이 좋습니다.

도구 모음 프레임워크는 추가 리소스 수준(예: 사용하지 않습니다. 기본적으로 규칙에 추상 종속 항목이 있다고 선언합니다. 대상 계열 (도구 모음 유형)의 일부 구성원 및 Bazel 특정 대상 (툴체인)으로 문제를 자동으로 해결합니다. 제약조건에 따라 다르기 때문입니다. 규칙 작성자 및 대상 작성자 모두 아님 사용 가능한 플랫폼과 도구 모음을 완전히 이해해야 합니다.

도구 모음을 사용하는 규칙 작성

도구 모음 프레임워크에서는 규칙이 도구에 직접 종속되는 대신 대신 도구 모음 유형에 종속됩니다. 도구 모음 유형은 단순한 대상임 서로 다른 여러 모델에서 동일한 역할을 수행하는 도구 클래스를 지원합니다 예를 들어, 막대를 나타내는 유형을 선언할 수 있습니다. 있습니다.

# 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"],
)

이제 구현 함수가 ctx.toolchains에서 이 종속 항목에 액세스합니다. ctx.attr 대신 도구 모음 유형을 키로 사용합니다.

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이 도구 모음 종속 항목을 해결한 모든 대상의 ID를 반환합니다. 이 ToolchainInfo 객체가 기본 도구의 규칙에 의해 설정됩니다. 다음 섹션에서 래핑하는 barcinfo 필드가 있도록 이 규칙이 정의됩니다. BarcInfo 객체

도구 모음을 대상으로 확인하는 Bazel의 절차 설명 아래를 참고하세요. 확인된 도구 모음 대상만 실제로 후보의 전체 공간이 아닌 bar_binary 타겟의 종속 항목을 설정함 있습니다.

필수 및 선택 툴체인

기본적으로 규칙이 베어 라벨을 사용하여 도구 모음 유형의 종속 항목을 표현하는 경우 도구 모음 유형은 필수로 간주됩니다. Bazel이 있다면 일치하는 도구 모음을 찾을 수 없습니다( 툴체인 해결 방법)을 포함해야 합니다. 오류가 발생하고 분석이 중단됩니다.

대신 다음과 같이 선택적 도구 모음 유형 종속 항목을 선언할 수 있습니다. 다음과 같습니다.

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 함수의 기본값은 필수 항목입니다.

다음 양식을 사용할 수 있습니다.

  • 필수 도구 모음 유형: <ph type="x-smartling-placeholder">
      </ph>
    • 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)]
  • 선택적 도구 모음 유형은 다음과 같습니다. <ph type="x-smartling-placeholder">
      </ph>
    • 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),
    ],
)

같은 규칙에서 양식을 혼합할 수도 있습니다. 그러나 도구 모음 유형이 여러 번 나열되므로 가장 엄격한 버전을 따릅니다. 필수가 선택사항보다 더 엄격합니다

도구 모음을 사용하는 관점 작성

관점은 규칙과 동일한 도구 모음 API에 액세스할 수 있습니다. 필수 항목을 정의할 수 있습니다. 컨텍스트를 통해 도구 모음에 액세스하고 이를 사용하여 새 도구 모음 생성 작업을 수행할 수 있습니다

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 []

도구 모음 정의

특정 도구 모음 유형에 맞게 도구 모음을 정의하려면 다음 세 가지 항목이 필요합니다.

  1. 도구 또는 도구 모음의 종류를 나타내는 언어별 규칙입니다. 작성자: 규칙 이름에는 '_ Toolkit'이 접미사로 붙습니다.

    1. 참고: \_toolchain 규칙은 빌드 작업을 만들 수 없습니다. 대신 다른 규칙에서 아티팩트를 수집하여 도구 모음을 사용하는 규칙을 생성합니다. 이 규칙은 빌드할 수 있습니다
  2. 도구 또는 도구의 버전을 나타내는 이 규칙 유형의 여러 대상 사용할 수 있습니다

  3. 이러한 각 타겟의 경우 일반 캠페인의 toolchain 드림 도구 모음 프레임워크에서 사용하는 메타데이터를 제공합니다. 이 toolchain 대상은 이 도구 모음과 관련된 toolchain_type도 참조합니다. 즉, 지정된 _toolchain 규칙이 toolchain_type, 그리고 다음을 사용하는 toolchain 인스턴스에서만 규칙이 toolchain_type와 연결된 이 _toolchain 규칙

실행 중인 예시에서는 bar_toolchain 규칙의 정의가 다음과 같습니다. Google의 예에는 컴파일러만 있지만 링커와 같은 다른 도구도 그 아래에 그룹화되어 있습니다.

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 및 도구 모음 유형입니다. ToolchainInfostruct와 같이 임의의 필드-값을 보유할 수 있습니다. 쌍. 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",
)

마지막으로 두 bar_toolchain 타겟의 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 정의 대상이 모두 별개일 수 없음 패키지입니다

go_toolchain를 참고하세요. 를 참조하세요.

툴체인 및 구성

규칙 작성자에게 중요한 질문은 bar_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 매개변수의 의미는 약간 다릅니다.

도구 모음을 통한 대상('상위'라고 함)에서 도구 모음으로의 종속 항목 해상도는 '도구 모음'이라는 특수 구성 전환을 사용합니다. 전환'이라고 표시됩니다. 도구 모음 전환은 다음과 같은 점을 제외하고 구성을 동일하게 유지합니다. 툴체인에 대해 실행 플랫폼이 동일하도록 강제합니다. 그렇지 않으면 툴체인에 대한 툴체인 해상도에서 원하는 대로 동일하지는 않습니다. 이 도구 모음의 모든 exec 종속 항목을 다음에서 실행할 수 있도록 합니다. 빌드 작업을 실행할 수 있습니다. cfg = "target"를 사용하거나 'target'이 기본값이므로 cfg을 지정하지 않는 도구 모음의 종속 항목은 다음과 같습니다. 상위 요소와 동일한 타겟 플랫폼용으로 빌드됩니다. 이를 통해 도구 모음 규칙으로 다음 작업을 할 수 있습니다. 라이브러리 (위의 system_lib 속성)와 도구( compiler 속성)을 이 속성이 필요한 빌드 규칙에 추가해야 합니다. 시스템 라이브러리 최종 아티팩트에 연결되므로 동일한 아티팩트에 맞게 빌드해야 반면 컴파일러는 빌드 중에 호출되는 도구이며 실행할 수 있습니다

도구 모음 등록 및 빌드

이 시점에서 모든 빌딩 블록이 조립되었으며, Bazel의 해결 절차에 사용할 수 있는 툴체인입니다. 이는 다음 명령어를 사용하여 MODULE.bazel 파일에 도구 모음을 등록합니다. register_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/...",
)

대상 패턴을 사용하여 도구 모음을 등록할 때 등록되는 개별 도구 모음은 다음 규칙에 따라 결정됩니다.

  • 패키지의 하위 패키지에 정의된 툴체인은 도구 모음에 포함되어 있습니다.
  • 패키지 내에서 도구 모음은 있습니다.

이제 도구 모음 유형에 종속되는 대상을 빌드할 때 적절한 대상 및 실행 플랫폼에 따라 선택됩니다.

# 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:barc_linux_toolchain에 대한 //bar_tools:toolchain_type 참조. 이렇게 하면 //bar_tools:barc_linux가 빌드되지만 빌드되지는 않습니다. //bar_tools:barc_windows

툴체인 해결

도구 모음을 사용하는 각 대상에 대해 Bazel의 도구 모음 확인 절차 대상의 구체적인 도구 모음 종속 항목을 결정합니다. 절차는 필요한 툴체인 유형, 대상 플랫폼, 그리고 가용한 도구 모음의 목록을 설명합니다. 출력 각 도구 모음 유형과 선택한 실행에 대해 선택된 도구 모음 현재 타겟의 플랫폼입니다.

사용 가능한 실행 플랫폼과 도구 모음은 외부 종속 항목 그래프를 통해 register_execution_platforms 드림 및 통화 register_toolchainsMODULE.bazel 파일 추가 실행 플랫폼 및 도구 모음도 명령줄을 사용하여 --extra_execution_platforms 드림 및 --extra_toolchains 호스트 플랫폼은 사용 가능한 실행 플랫폼으로 자동 포함됩니다. 사용 가능한 플랫폼과 툴체인은 결정론을 위해 순서가 지정된 목록으로 추적되며, 목록에 있는 이전 항목이 우선 적용됩니다.

사용 가능한 도구 모음 집합은 우선순위에 따라 --extra_toolchainsregister_toolchains:

  1. --extra_toolchains를 사용하여 등록된 도구 모음이 먼저 추가됩니다. ( 마지막 도구 모음의 우선순위가 가장 높습니다.)
  2. 전이 외부에서 register_toolchains를 사용하여 등록된 도구 모음 종속 항목 그래프를 다음과 같은 순서로 표시합니다. (이 항목 내에서 첫 번째 우선순위가 가장 높다고 언급했습니다.)
    1. 루트 모듈에 의해 등록된 도구 모음 (예:MODULE.bazel 작업공간 루트)
    2. 사용자의 WORKSPACE 파일에 등록된 도구 모음(모든 매크로가 호출되고
    3. 루트가 아닌 모듈에 의해 등록된 툴체인 (즉, 루트 모듈, 그 종속 항목 등)
    4. "WORKSPACE 접미사"에 등록된 툴체인 이것은 Bazel 설치와 함께 번들로 제공되는 특정 기본 규칙을 참조하세요.

참고: :all, :*, /...는 Bazel의 패키지로 정렬됨 사전적 순서를 사용하는 로드 메커니즘을 사용합니다.

해결 단계는 다음과 같습니다.

  1. target_compatible_with 또는 exec_compatible_with 절은 다음과 일치합니다. 목록에 있는 각 constraint_value에 대해 그 constraint_value입니다 (명시적으로 또는 기본값으로).

    플랫폼에 없는 constraint_settingconstraint_value가 있는 경우 일치에 영향을 미치지 않습니다.

  2. 빌드 중인 타겟이 exec_compatible_with 속성 (또는 규칙 정의가 exec_compatible_with 인수). 사용 가능한 실행 플랫폼 목록이 필터링되어 일치하지 않는 모든 종속 항목을 반환합니다

  3. 사용 가능한 도구 모음 목록이 모든 도구 모음을 삭제하도록 필터링됨 현재 구성과 일치하지 않는 target_settings를 지정합니다.

  4. 사용 가능한 각 실행 플랫폼에 대해 각 도구 모음 유형을 이 실행과 호환되는 첫 번째 사용 가능한 도구 모음(있는 경우) 대상 플랫폼이 다릅니다

  5. 호환되는 필수 도구 모음을 찾지 못한 실행 플랫폼 도구 모음 유형 중 하나에 대한 항목은 제외되었습니다. 나머지 플랫폼 중에서 첫 번째가 현재 대상의 실행 플랫폼이 되며 연결된 도구 모음 (있는 경우)은 타겟의 종속 항목이 됩니다.

선택한 실행 플랫폼이 대상 생성합니다.

동일한 타겟을 여러 구성으로 빌드할 수 있는 경우 (예: 같은 빌드 내에서) 해결 절차가 적용됩니다. 각 타겟 버전에 독립적으로 수행할 수 있습니다

규칙에서 실행 그룹을 사용하는 경우에는 각 실행이 그룹은 도구 모음 확인을 개별적으로 수행하며, 각 도구마다 자체적인 실행이 있습니다. 플랫폼 및 도구 모음으로 이루어져 있습니다

디버깅 도구 모음

기존 규칙에 도구 모음 지원을 추가하는 경우 --toolchain_resolution_debug=regex 플래그. 툴체인 확인 중에 이 플래그는 정규식 변수와 일치하는 도구 모음 유형 또는 대상 이름에 관한 상세 출력을 제공합니다. 나 .*를 사용하여 모든 정보를 출력할 수 있습니다. Bazel이 도구 모음 이름을 출력함 확인하며 해결 프로세스 중에 건너뜁니다.

도구 모음의 cquery 종속 항목을 확인하려는 경우 cquery--transitions 플래그를 사용합니다.

# 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