플랫폼

소개

Bazel은 다양한 하드웨어, 운영체제, 시스템 구성에서 코드를 빌드하고 테스트할 수 있습니다. 여기에는 링커, 컴파일러와 같은 다양한 버전의 빌드 도구가 포함될 수 있습니다. 이러한 복잡성을 관리하기 위해 Bazel에는 제약 조건플랫폼이라는 개념이 있습니다.

제약 조건은 빌드 또는 프로덕션 머신의 고유한 속성입니다. 일반적인 제약 조건은 CPU 아키텍처, GPU 유무, 로컬에 설치된 컴파일러 버전입니다. 하지만 제약 조건은 빌드 작업을 오케스트레이션할 때 머신을 의미 있게 구분하는 모든 것이 될 수 있습니다.

플랫폼은 완전한 머신을 지정하는 제약 조건 모음입니다. Bazel은 이 개념을 사용하여 개발자가 빌드할 머신, 컴파일 및 테스트 작업을 실행할 머신, 도구 모음 빌드 작업이 컴파일되어야 하는 머신을 선택할 수 있도록 합니다.

개발자는 제약 조건을 사용하여 빌드 규칙에서 맞춤 속성이나 종속 항목을 선택할 수도 있습니다. 예: '빌드가 Arm 머신을 타겟팅하는 경우 src_arm.cc 사용'

플랫폼 유형

Bazel은 플랫폼이 수행할 수 있는 세 가지 역할을 인식합니다.

  • 호스트 - Bazel 자체가 실행되는 플랫폼입니다.
  • 실행 - 컴파일 작업을 실행하여 빌드 출력을 생성하는 플랫폼입니다.
  • 타겟 - 빌드되는 코드가 실행되어야 하는 플랫폼입니다.

빌드는 일반적으로 플랫폼과 세 가지 관계를 갖습니다.

  • 단일 플랫폼 빌드 - 호스트, 실행, 타겟 플랫폼이 동일합니다. 예를 들어 원격 실행 없이 개발자 머신에서 빌드한 다음 동일한 머신에서 빌드된 바이너리를 실행합니다.

  • 크로스 컴파일 빌드 - 호스트 및 실행 플랫폼은 동일하지만 타겟 플랫폼은 다릅니다. 예를 들어 원격 실행 없이 Macbook Pro에서 iOS 앱을 빌드하는 경우입니다.

  • 멀티 플랫폼 빌드 - 호스트, 실행, 타겟 플랫폼이 모두 다릅니다. 예를 들어 Macbook Pro에서 iOS 앱을 빌드하고 원격 Linux 머신을 사용하여 Xcode가 필요하지 않은 C++ 작업을 컴파일합니다.

플랫폼 지정

개발자가 플랫폼을 사용하는 가장 일반적인 방법은 --platforms 플래그로 원하는 타겟 머신을 지정하는 것입니다.

$ bazel build //:my_linux_app --platforms=//myplatforms:linux_x86

조직은 일반적으로 자체 플랫폼 정의를 유지합니다. 빌드 머신 설정이 조직마다 다르기 때문입니다.

--platforms을 설정하지 않으면 기본값은 @platforms//host입니다. 이는 Bazel이 실행되는 동일한 머신을 타겟팅하도록 호스트 머신의 OS 및 CPU 속성을 자동 감지하도록 특별히 정의됩니다. 빌드 규칙은 @platforms/os@platforms/cpu 제약 조건으로 이러한 속성을 선택할 수 있습니다.

일반적으로 유용한 제약 조건 및 플랫폼

생태계를 일관되게 유지하기 위해 Bazel팀은 가장 인기 있는 CPU 아키텍처와 운영체제의 제약 조건 정의가 포함된 저장소를 유지관리합니다. 이러한 모든 항목은 https://github.com/bazelbuild/platforms에 정의되어 있습니다.

Bazel은 다음 특수 플랫폼 정의와 함께 제공됩니다. @platforms//host (@bazel_tools//tools:host_platform로 별칭 지정됨). 이는 Bazel이 실행되는 머신의 OS 및 CPU 속성을 자동 감지합니다.

제약 조건 정의

제약 조건은 constraint_settingconstraint_value 빌드 규칙으로 모델링됩니다.

constraint_setting는 속성 유형을 선언합니다. 예를 들면 다음과 같습니다.

constraint_setting(name = "cpu")

constraint_value은 해당 속성의 가능한 값을 선언합니다.

constraint_value(
    name = "x86",
    constraint_setting = ":cpu"
)

이러한 항목은 플랫폼을 정의하거나 플랫폼에서 빌드 규칙을 맞춤설정할 때 라벨로 참조할 수 있습니다. 위의 예가 cpus/BUILD에 정의된 경우 x86 제약 조건을 //cpus:x86로 참조할 수 있습니다.

표시 가능성이 허용되는 경우 자체 값을 정의하여 기존 constraint_setting를 확장할 수 있습니다.

플랫폼 정의

platform 빌드 규칙은 플랫폼을 constraint_value의 컬렉션으로 정의합니다.

platform(
    name = "linux_x86",
    constraint_values = [
        "//oses:linux",
        "//cpus:x86",
    ],
)

//oses:linux//cpus:x86 제약 조건을 모두 충족해야 하는 머신을 모델링합니다.

플랫폼에는 지정된 constraint_setting에 대해 constraint_value가 하나만 있을 수 있습니다. 즉, 두 번째 값을 모델링하는 다른 constraint_setting 유형을 만들지 않는 한 플랫폼에 CPU가 두 개 있을 수 없습니다.

호환되지 않는 타겟 건너뛰기

특정 타겟 플랫폼용으로 빌드할 때는 해당 플랫폼에서 작동하지 않는 타겟을 건너뛰는 것이 좋습니다. 예를 들어 //...를 사용하여 Linux 머신에서 빌드할 때 Windows 기기 드라이버에서 많은 컴파일러 오류가 발생할 수 있습니다. target_compatible_with 속성을 사용하여 코드에 있는 타겟 플랫폼 제약 조건을 Bazel에 알립니다.

이 속성을 가장 간단하게 사용하면 타겟이 단일 플랫폼으로 제한됩니다. 모든 제약 조건을 충족하지 않는 플랫폼에서는 타겟이 빌드되지 않습니다. 다음 예시에서는 win_driver_lib.cc을 64비트 Windows로 제한합니다.

cc_library(
    name = "win_driver_lib",
    srcs = ["win_driver_lib.cc"],
    target_compatible_with = [
        "@platforms//cpu:x86_64",
        "@platforms//os:windows",
    ],
)

:win_driver_lib는 64비트 Windows로 빌드하는 데만 호환되고 그 외에는 모두 호환되지 않습니다. 비호환성은 전이적입니다. 호환되지 않는 타겟에 간접적으로 의존하는 타겟은 호환되지 않는 것으로 간주됩니다.

타겟은 언제 건너뛰나요?

타겟이 호환되지 않는 것으로 간주되고 타겟 패턴 확장으로 빌드에 포함되면 타겟이 건너뛰어집니다. 예를 들어 다음 두 호출은 대상 패턴 확장 시 발견된 호환되지 않는 대상을 건너뜁니다.

$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all

test_suite의 호환되지 않는 테스트는 명령줄에서 test_suite--expand_test_suites와 함께 지정된 경우와 마찬가지로 건너뜁니다. 즉, 명령줄의 test_suite 타겟은 :all...와 같이 동작합니다. --noexpand_test_suites를 사용하면 확장되지 않으며 호환되지 않는 테스트가 있는 test_suite 타겟도 호환되지 않습니다.

명령줄에서 호환되지 않는 타겟을 명시적으로 지정하면 오류 메시지가 표시되고 빌드가 실패합니다.

$ bazel build --platforms=//:myplatform //:target_incompatible_with_myplatform
...
ERROR: Target //:target_incompatible_with_myplatform is incompatible and cannot be built, but was explicitly requested.
...
FAILED: Build did NOT complete successfully

--skip_incompatible_explicit_targets가 사용 설정된 경우 호환되지 않는 명시적 타겟은 자동으로 건너뜁니다.

더 표현력이 풍부한 제약 조건

제약 조건을 더 유연하게 표현하려면 플랫폼에서 충족하지 않는 @platforms//:incompatible constraint_value을 사용하세요.

@platforms//:incompatible와 함께 select()를 사용하여 더 복잡한 제한사항을 표현합니다. 예를 들어 기본 OR 로직을 구현하는 데 사용합니다. 다음은 macOS 및 Linux와 호환되지만 다른 플랫폼과는 호환되지 않는 라이브러리를 표시합니다.

cc_library(
    name = "unixish_lib",
    srcs = ["unixish_lib.cc"],
    target_compatible_with = select({
        "@platforms//os:osx": [],
        "@platforms//os:linux": [],
        "//conditions:default": ["@platforms//:incompatible"],
    }),
)

위 내용은 다음과 같이 해석할 수 있습니다.

  1. macOS를 타겟팅하는 경우 타겟에 제약이 없습니다.
  2. Linux를 타겟팅하는 경우 타겟에 제약이 없습니다.
  3. 그렇지 않으면 타겟에 @platforms//:incompatible 제약 조건이 있습니다. @platforms//:incompatible는 플랫폼에 속하지 않으므로 타겟이 호환되지 않는 것으로 간주됩니다.

제약 조건의 가독성을 높이려면 skylibselects.with_or()를 사용하세요.

역 호환성도 비슷한 방식으로 표현할 수 있습니다. 다음 예에서는 ARM을 제외한 모든 항목과 호환되는 라이브러리를 설명합니다.

cc_library(
    name = "non_arm_lib",
    srcs = ["non_arm_lib.cc"],
    target_compatible_with = select({
        "@platforms//cpu:arm": ["@platforms//:incompatible"],
        "//conditions:default": [],
    }),
)

bazel cquery를 사용하여 호환되지 않는 타겟 감지

bazel cqueryStarlark 출력 형식에서 IncompatiblePlatformProvider을 사용하여 호환되지 않는 타겟과 호환되는 타겟을 구분할 수 있습니다.

이를 사용하여 호환되지 않는 타겟을 필터링할 수 있습니다. 아래 예시에서는 호환되는 타겟의 라벨만 출력합니다. 호환되지 않는 타겟은 인쇄되지 않습니다.

$ cat example.cquery

def format(target):
  if "IncompatiblePlatformProvider" not in providers(target):
    return target.label
  return ""


$ bazel cquery //... --output=starlark --starlark:file=example.cquery

알려진 문제

호환되지 않는 타겟은 공개 범위 제한을 무시합니다.