구성 가능한 빌드 속성

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.
문제 신고 소스 보기

일반적으로 select()로 알려진 구성 가능한 속성은 사용자가 명령줄에서 빌드 규칙 속성 값을 전환할 수 있는 Bazel 기능입니다.

예를 들어 아키텍처에 적합한 구현을 자동으로 선택하는 멀티플랫폼 라이브러리나 빌드 시 맞춤설정 가능한 기능 구성 가능한 바이너리에 사용할 수 있습니다.

# myapp/BUILD

cc_binary(
    name = "mybinary",
    srcs = ["main.cc"],
    deps = select({
        ":arm_build": [":arm_lib"],
        ":x86_debug_build": [":x86_dev_lib"],
        "//conditions:default": [":generic_lib"],
    }),
)

config_setting(
    name = "arm_build",
    values = {"cpu": "arm"},
)

config_setting(
    name = "x86_debug_build",
    values = {
        "cpu": "x86",
        "compilation_mode": "dbg",
    },
)

이렇게 하면 명령줄에서 플래그에 따라 deps를 '선택'하는 cc_binary이 선언됩니다. 특히 deps는 다음과 같습니다.

명령어 deps =
bazel build //myapp:mybinary --cpu=arm [":arm_lib"]
bazel build //myapp:mybinary -c dbg --cpu=x86 [":x86_dev_lib"]
bazel build //myapp:mybinary --cpu=ppc [":generic_lib"]
bazel build //myapp:mybinary -c dbg --cpu=ppc [":generic_lib"]

select()config_setting 대상을 참조하는 라벨인 구성 조건에 따라 선택될 값의 자리표시자 역할을 합니다. 구성 가능한 속성에서 select()를 사용하면 서로 다른 조건이 적용될 때 속성에서 다른 값을 효과적으로 채택할 수 있습니다.

일치는 명확해야 합니다. 정확히 하나의 조건이 일치해야 하며, 여러 조건이 일치하는 경우 하나의 values은 다른 모든 요소의 엄격한 상위 집합이어야 합니다. 예를 들어 values = {"cpu": "x86", "compilation_mode": "dbg"}values = {"cpu": "x86"}의 명확한 전문 분야입니다. 기본 제공 조건 //conditions:default는 다른 항목이 없을 때 자동으로 일치합니다.

이 예에서는 deps를 사용하지만 select()srcs, resources, cmd 및 대부분의 다른 속성에서도 잘 작동합니다. 극히 일부의 속성만 구성할 수 없으며, 명시적으로 주석 처리됩니다. 예를 들어 config_setting의 자체 values 속성은 구성할 수 없습니다.

select() 및 종속 항목

특정 속성은 타겟 아래의 모든 전이 종속 항목의 빌드 매개변수를 변경합니다. 예를 들어 genruletools는 Bazel을 실행하는 머신의 CPU로 --cpu를 변경합니다 (크로스 컴파일 덕분에 대상이 빌드된 CPU와 다를 수 있음). 이를 구성 전환이라고 합니다.

부여됨

#myapp/BUILD

config_setting(
    name = "arm_cpu",
    values = {"cpu": "arm"},
)

config_setting(
    name = "x86_cpu",
    values = {"cpu": "x86"},
)

genrule(
    name = "my_genrule",
    srcs = select({
        ":arm_cpu": ["g_arm.src"],
        ":x86_cpu": ["g_x86.src"],
    }),
    tools = select({
        ":arm_cpu": [":tool1"],
        ":x86_cpu": [":tool2"],
    }),
)

cc_binary(
    name = "tool1",
    srcs = select({
        ":arm_cpu": ["armtool.cc"],
        ":x86_cpu": ["x86tool.cc"],
    }),
)

실행 중

$ bazel build //myapp:my_genrule --cpu=arm

x86 개발자 머신에서 빌드가 g_arm.src, tool1, x86tool.cc에 바인딩됩니다. my_genrule에 연결된 두 select 모두 my_genrule를 포함하는 --cpu=arm의 빌드 매개변수를 사용합니다. tools 속성은 tool1 및 전이 종속 항목의 --cpux86로 변경합니다. tool1select--cpu=x86를 포함하는 tool1의 빌드 매개변수를 사용합니다.

구성 조건

구성 가능한 속성의 각 키는 config_setting 또는 constraint_value의 라벨 참조입니다.

config_setting은 예상되는 명령줄 플래그 설정의 모음입니다. 이러한 문제를 타겟에 캡슐화하면 사용자가 여러 위치에서 참조할 수 있는 '표준' 조건을 쉽게 유지할 수 있습니다.

constraint_value멀티 플랫폼 동작을 지원합니다.

기본 제공 플래그

--cpu와 같은 플래그는 Bazel에 내장되어 있으며, 빌드 도구가 기본적으로 모든 프로젝트의 모든 빌드에 사용됩니다. 이는 config_settingvalues 속성으로 지정됩니다.

config_setting(
    name = "meaningful_condition_name",
    values = {
        "flag1": "value1",
        "flag2": "value2",
        ...
    },
)

flagN은 플래그 이름입니다 (--가 없으므로 "--cpu" 대신 "cpu"). valueN은 플래그의 예상 값입니다. values모든 항목이 일치하면 :meaningful_condition_name가 일치합니다. 관련 없는 주문입니다.

valueN은 명령줄에 설정된 것처럼 파싱됩니다. 이는 다음을 의미합니다.

  • values = { "compilation_mode": "opt" } 일치 bazel build -c opt
  • values = { "force_pic": "true" } 일치 bazel build --force_pic=1
  • values = { "force_pic": "0" } 일치 bazel build --noforce_pic

config_setting는 타겟 동작에 영향을 미치는 플래그만 지원합니다. 예를 들어 --show_progress은 Bazel이 사용자에게 진행률을 보고하는 방식에만 영향을 미치므로 허용되지 않습니다. 대상은 이 플래그를 사용하여 결과를 생성할 수 없습니다. 지원되는 플래그의 정확한 집합은 문서화되지 않습니다. 실제로 '의미 있는' 대부분의 플래그가 효과가 있습니다.

커스텀 플래그

Starlark 빌드 설정을 사용하여 자체 프로젝트별 플래그를 모델링할 수 있습니다. 기본 제공 플래그와 달리 빌드 대상으로 정의되므로 Bazel은 대상 라벨로 이를 참조합니다.

config_settingflag_values 속성으로 트리거됩니다.

config_setting(
    name = "meaningful_condition_name",
    flag_values = {
        "//myflags:flag1": "value1",
        "//myflags:flag2": "value2",
        ...
    },
)

동작은 기본 제공 플래그와 동일합니다. 작동 예는 여기를 참고하세요.

--define는 맞춤 플래그의 대체 문법입니다 (예: --define foo=bar). 이는 values 속성(values = {"define": "foo=bar"}) 또는 define_values 속성(define_values = {"foo": "bar"})으로 표현될 수 있습니다. --define는 이전 버전과의 호환성을 위해서만 지원됩니다. 가능하면 Starlark 빌드 설정을 사용하는 것이 좋습니다.

values, flag_values, define_values는 독립적으로 평가됩니다. 모든 값의 모든 값이 일치하면 config_setting가 일치합니다.

기본 조건

일치하는 다른 조건이 없을 때 기본 제공 조건 //conditions:default는 일치합니다.

'정확히 하나의 일치' 규칙으로 인해 일치 및 기본 조건이 없는 구성 가능한 속성은 "no matching conditions" 오류를 표시합니다. 이를 통해 다음과 같이 예기치 않은 설정의 자동 실패로부터 보호할 수 있습니다.

# myapp/BUILD

config_setting(
    name = "x86_cpu",
    values = {"cpu": "x86"},
)

cc_library(
    name = "x86_only_lib",
    srcs = select({
        ":x86_cpu": ["lib.cc"],
    }),
)
$ bazel build //myapp:x86_only_lib --cpu=arm
ERROR: Configurable attribute "srcs" doesn't match this configuration (would
a default condition help?).
Conditions checked:
  //myapp:x86_cpu

더 명확한 오류를 확인하려면 select()no_match_error 속성을 사용하여 커스텀 메시지를 설정하면 됩니다.

플랫폼

명령줄에 여러 플래그를 지정하는 기능은 유연하지만 대상을 빌드할 때마다 개별적으로 설정하는 것은 번거로울 수 있습니다. 플랫폼을 사용하면 이를 간단한 번들로 통합할 수 있습니다.

# myapp/BUILD

sh_binary(
    name = "my_rocks",
    srcs = select({
        ":basalt": ["pyroxene.sh"],
        ":marble": ["calcite.sh"],
        "//conditions:default": ["feldspar.sh"],
    }),
)

config_setting(
    name = "basalt",
    constraint_values = [
        ":black",
        ":igneous",
    ],
)

config_setting(
    name = "marble",
    constraint_values = [
        ":white",
        ":metamorphic",
    ],
)

# constraint_setting acts as an enum type, and constraint_value as an enum value.
constraint_setting(name = "color")
constraint_value(name = "black", constraint_setting = "color")
constraint_value(name = "white", constraint_setting = "color")
constraint_setting(name = "texture")
constraint_value(name = "smooth", constraint_setting = "texture")
constraint_setting(name = "type")
constraint_value(name = "igneous", constraint_setting = "type")
constraint_value(name = "metamorphic", constraint_setting = "type")

platform(
    name = "basalt_platform",
    constraint_values = [
        ":black",
        ":igneous",
    ],
)

platform(
    name = "marble_platform",
    constraint_values = [
        ":white",
        ":smooth",
        ":metamorphic",
    ],
)

명령줄에서 플랫폼을 지정할 수 있습니다. 이렇게 하면 플랫폼의 constraint_values 하위 집합이 포함된 config_setting가 활성화되어 select() 표현식에서 config_setting가 일치될 수 있습니다.

예를 들어 my_rockssrcs 속성을 calcite.sh로 설정하려면

bazel build //my_app:my_rocks --platforms=//myapp:marble_platform

플랫폼이 없다면

bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic

select()constraint_value을 직접 읽을 수도 있습니다.

constraint_setting(name = "type")
constraint_value(name = "igneous", constraint_setting = "type")
constraint_value(name = "metamorphic", constraint_setting = "type")
sh_binary(
    name = "my_rocks",
    srcs = select({
        ":igneous": ["igneous.sh"],
        ":metamorphic" ["metamorphic.sh"],
    }),
)

따라서 단일 값만 확인하면 되는 상용구 config_setting가 필요하지 않습니다.

플랫폼은 아직 개발 중입니다. 자세한 내용은 문서를 참고하세요.

select() 조합

select은 동일한 속성에 여러 번 표시될 수 있습니다.

sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"] +
           select({
               ":armeabi_mode": ["armeabi_src.sh"],
               ":x86_mode": ["x86_src.sh"],
           }) +
           select({
               ":opt_mode": ["opt_extras.sh"],
               ":dbg_mode": ["dbg_extras.sh"],
           }),
)

select은(는) 다른 select에 표시될 수 없습니다. selects를 중첩해야 하고 속성이 다른 타겟을 값으로 사용하는 경우 중간 대상을 사용합니다.

sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":armeabi_mode": [":armeabi_lib"],
        ...
    }),
)

sh_library(
    name = "armeabi_lib",
    srcs = select({
        ":opt_mode": ["armeabi_with_opt.sh"],
        ...
    }),
)

여러 조건이 일치할 때 일치시킬 select이 필요하다면 AND 체이닝을 사용하는 것이 좋습니다.

OR 체이닝

다음을 고려하세요.

sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":config1": [":standard_lib"],
        ":config2": [":standard_lib"],
        ":config3": [":standard_lib"],
        ":config4": [":special_lib"],
    }),
)

대부분의 조건은 동일한 dep로 평가됩니다. 그러나 이 구문은 읽고 유지관리하기 어렵습니다. [":standard_lib"]를 여러 번 반복하지 않는 것이 좋습니다.

한 가지 옵션은 값을 BUILD 변수로 사전 정의하는 것입니다.

STANDARD_DEP = [":standard_lib"]

sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":config1": STANDARD_DEP,
        ":config2": STANDARD_DEP,
        ":config3": STANDARD_DEP,
        ":config4": [":special_lib"],
    }),
)

이렇게 하면 종속 항목을 더 쉽게 관리할 수 있습니다. 하지만 여전히 불필요한 중복이 발생합니다.

보다 직접적인 지원을 받으려면 다음 중 하나를 사용하세요.

selects.with_or

Skylibselects 모듈에 있는 with_or 매크로는 select 내에서 직접 OR 조건을 지원합니다.

load("@bazel_skylib//lib:selects.bzl", "selects")
sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = selects.with_or({
        (":config1", ":config2", ":config3"): [":standard_lib"],
        ":config4": [":special_lib"],
    }),
)

selects.config_setting_group

Skylibselects 모듈에 있는 config_setting_group 매크로는 여러 config_settingOR를 지원합니다.

load("@bazel_skylib//lib:selects.bzl", "selects")
config_setting(
    name = "config1",
    values = {"cpu": "arm"},
)
config_setting(
    name = "config2",
    values = {"compilation_mode": "dbg"},
)
selects.config_setting_group(
    name = "config1_or_2",
    match_any = [":config1", ":config2"],
)
sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":config1_or_2": [":standard_lib"],
        "//conditions:default": [":other_lib"],
    }),
)

selects.with_or와 달리 여러 대상이 서로 다른 속성에서 :config1_or_2를 공유할 수 있습니다.

조건이 서로 명확하지 않으면 여러 조건을 일치시킬 수 없습니다. 자세한 내용은 여기를 참고하세요.

AND 체이닝

여러 조건이 일치할 때 일치시킬 select 브랜치가 필요한 경우 Skylib 매크로 config_setting_group을 사용합니다.

config_setting(
    name = "config1",
    values = {"cpu": "arm"},
)
config_setting(
    name = "config2",
    values = {"compilation_mode": "dbg"},
)
selects.config_setting_group(
    name = "config1_and_2",
    match_all = [":config1", ":config2"],
)
sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":config1_and_2": [":standard_lib"],
        "//conditions:default": [":other_lib"],
    }),
)

OR 체이닝과 달리 기존 config_settingselect 내에서 직접 AND될 수 없습니다. 이를 명시적으로 config_setting_group에 래핑해야 합니다.

커스텀 오류 메시지

기본적으로 일치하는 조건이 없으면 select()와 연결된 대상이 다음 오류와 함께 실패합니다.

ERROR: Configurable attribute "deps" doesn't match this configuration (would
a default condition help?).
Conditions checked:
  //tools/cc_target_os:darwin
  //tools/cc_target_os:android

no_match_error 속성을 사용하여 맞춤설정할 수 있습니다.

cc_library(
    name = "my_lib",
    deps = select(
        {
            "//tools/cc_target_os:android": [":android_deps"],
            "//tools/cc_target_os:windows": [":windows_deps"],
        },
        no_match_error = "Please build with an Android or Windows toolchain",
    ),
)
$ bazel build //myapp:my_lib
ERROR: Configurable attribute "deps" doesn't match this configuration: Please
build with an Android or Windows toolchain

규칙 호환성

규칙 구현은 구성 가능한 속성의 확인된 값을 수신합니다. 예를 들면 다음과 같습니다.

# myapp/BUILD

some_rule(
    name = "my_target",
    some_attr = select({
        ":foo_mode": [":foo"],
        ":bar_mode": [":bar"],
    }),
)
$ bazel build //myapp/my_target --define mode=foo

규칙 구현 코드에서는 ctx.attr.some_attr[":foo"]로 표시됩니다.

매크로는 select() 절을 허용하고 네이티브 규칙에 전달할 수 있습니다. 직접 조작할 수는 없습니다. 예를 들어 매크로가 변환되지 않고

select({"foo": "val"}, ...)

받는 사람

select({"foo": "val_with_suffix"}, ...)

여기에는 두 가지 이유가 있습니다.

첫째, select가 어떤 경로를 선택할지 알아야 하는 매크로는 플래그 값을 알기 전에 발생하는 Bazel의 로드 단계에서 평가되므로 작동할 수 없습니다. 이는 핵심 Bazel 설계 제한사항으로, 조만간 변경될 가능성이 낮습니다.

둘째, 기술적으로는 실행 가능하지만 모든 select 경로를 반복해야 하는 매크로에는 일관된 UI가 부족합니다. 이를 변경하려면 추가 설계가 필요합니다.

Bazel 쿼리 및 cquery

Bazel query은 Bazel의 로드 단계를 통해 작동합니다. 즉, 빌드에서 후반부 (분석 단계)까지 이러한 플래그가 평가되지 않으므로 대상에서 사용하는 명령줄 플래그는 알 수 없습니다. 따라서 선택된 select() 브랜치를 결정할 수 없습니다.

Bazel cquery은 Bazel의 분석 단계 후에 작동하므로 이 모든 정보를 가지고 select()를 정확하게 해결할 수 있습니다.

고려 사항:

load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
# myapp/BUILD

string_flag(
    name = "dog_type",
    build_setting_default = "cat"
)

cc_library(
    name = "my_lib",
    deps = select({
        ":long": [":foo_dep"],
        ":short": [":bar_dep"],
    }),
)

config_setting(
    name = "long",
    flag_values = {":dog_type": "dachshund"},
)

config_setting(
    name = "short",
    flag_values = {":dog_type": "pug"},
)

query:my_lib의 종속 항목에 근사치입니다.

$ bazel query 'deps(//myapp:my_lib)'
//myapp:my_lib
//myapp:foo_dep
//myapp:bar_dep

반면 cquery는 정확한 종속 항목을 표시합니다.

$ bazel cquery 'deps(//myapp:my_lib)' --//myapp:dog_type=pug
//myapp:my_lib
//myapp:bar_dep

FAQ

매크로에서 select()가 작동하지 않는 이유는 무엇인가요?

규칙에서 select()가 작동합니다. 자세한 내용은 규칙 호환성을 참조하세요.

이 질문은 일반적으로 select()가 매크로에서 작동하지 않는다는 것을 의미합니다. 규칙과는 다릅니다. 차이점에 대해서는 규칙매크로 관련 문서를 참조하세요. 다음은 엔드 투 엔드 예시입니다.

다음과 같이 규칙과 매크로를 정의합니다.

# myapp/defs.bzl

# Rule implementation: when an attribute is read, all select()s have already
# been resolved. So it looks like a plain old attribute just like any other.
def _impl(ctx):
    name = ctx.attr.name
    allcaps = ctx.attr.my_config_string.upper()  # This works fine on all values.
    print("My name is " + name + " with custom message: " + allcaps)

# Rule declaration:
my_custom_bazel_rule = rule(
    implementation = _impl,
    attrs = {"my_config_string": attr.string()},
)

# Macro declaration:
def my_custom_bazel_macro(name, my_config_string):
    allcaps = my_config_string.upper()  # This line won't work with select(s).
    print("My name is " + name + " with custom message: " + allcaps)

규칙과 매크로를 인스턴스화합니다.

# myapp/BUILD

load("//myapp:defs.bzl", "my_custom_bazel_rule")
load("//myapp:defs.bzl", "my_custom_bazel_macro")

my_custom_bazel_rule(
    name = "happy_rule",
    my_config_string = select({
        "//tools/target_cpu:x86": "first string",
        "//tools/target_cpu:ppc": "second string",
    }),
)

my_custom_bazel_macro(
    name = "happy_macro",
    my_config_string = "fixed string",
)

my_custom_bazel_macro(
    name = "sad_macro",
    my_config_string = select({
        "//tools/target_cpu:x86": "first string",
        "//tools/target_cpu:ppc": "other string",
    }),
)

sad_macro에서 select()을 처리할 수 없기 때문에 빌드가 실패합니다.

$ bazel build //myapp:all
ERROR: /myworkspace/myapp/BUILD:17:1: Traceback
  (most recent call last):
File "/myworkspace/myapp/BUILD", line 17
my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
File "/myworkspace/myapp/defs.bzl", line 4, in
  my_custom_bazel_macro
my_config_string.upper()
type 'select' has no method upper().
ERROR: error loading package 'myapp': Package 'myapp' contains errors.

sad_macro를 주석 처리하면 빌드가 성공합니다.

# Comment out sad_macro so it doesn't mess up the build.
$ bazel build //myapp:all
DEBUG: /myworkspace/myapp/defs.bzl:5:3: My name is happy_macro with custom message: FIXED STRING.
DEBUG: /myworkspace/myapp/hi.bzl:15:3: My name is happy_rule with custom message: FIRST STRING.

Bazel이 빌드의 명령줄 플래그를 읽기 전에 정의에 따라 매크로가 평가되므로 이는 변경할 수 없습니다. 즉, select()를 평가하기 위한 정보가 충분하지 않습니다.

그러나 매크로는 select()를 불투명 blob으로 규칙에 전달할 수 있습니다.

# myapp/defs.bzl

def my_custom_bazel_macro(name, my_config_string):
    print("Invoking macro " + name)
    my_custom_bazel_rule(
        name = name + "_as_target",
        my_config_string = my_config_string,
    )
$ bazel build //myapp:sad_macro_less_sad
DEBUG: /myworkspace/myapp/defs.bzl:23:3: Invoking macro sad_macro_less_sad.
DEBUG: /myworkspace/myapp/defs.bzl:15:3: My name is sad_macro_less_sad with custom message: FIRST STRING.

select()가 항상 true를 반환하는 이유는 무엇인가요?

정의에서 매크로 (규칙은 아님)는 select()를 평가할 수 없기 때문에 이를 시도하면 일반적으로 오류가 발생합니다.

ERROR: /myworkspace/myapp/BUILD:17:1: Traceback
  (most recent call last):
File "/myworkspace/myapp/BUILD", line 17
my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
File "/myworkspace/myapp/defs.bzl", line 4, in
  my_custom_bazel_macro
my_config_string.upper()
type 'select' has no method upper().

불리언은 자동으로 실패하는 특수 사례이므로 특별히 주의해야 합니다.

$ cat myapp/defs.bzl
def my_boolean_macro(boolval):
  print("TRUE" if boolval else "FALSE")

$ cat myapp/BUILD
load("//myapp:defs.bzl", "my_boolean_macro")
my_boolean_macro(
    boolval = select({
        "//tools/target_cpu:x86": True,
        "//tools/target_cpu:ppc": False,
    }),
)

$ bazel build //myapp:all --cpu=x86
DEBUG: /myworkspace/myapp/defs.bzl:4:3: TRUE.
$ bazel build //mypro:all --cpu=ppc
DEBUG: /myworkspace/myapp/defs.bzl:4:3: TRUE.

이는 매크로가 select()의 콘텐츠를 이해하지 못하기 때문에 발생합니다. 따라서 평가 대상은 select() 객체 자체입니다. Python 디자인 표준에 따라 매우 적은 수의 예외를 제외하고는 모든 객체가 자동으로 true를 반환합니다.

사전처럼 select()을 읽을 수 있나요?

Bazel이 빌드의 명령줄 매개변수가 무엇인지 알아보기 전에 매크로가 평가되기 때문에 매크로는 선택할 수 없습니다. 예를 들어 각 값에 접미사를 추가하기 위해 select()의 사전을 읽을 수 있나요?

개념적으로는 가능하지만 아직 Bazel 기능은 아닙니다. 지금 할 수 있는 작업은 사전을 준비한 다음 select()에 피드하는 것입니다.

$ cat myapp/defs.bzl
def selecty_genrule(name, select_cmd):
  for key in select_cmd.keys():
    select_cmd[key] += " WITH SUFFIX"
  native.genrule(
      name = name,
      outs = [name + ".out"],
      srcs = [],
      cmd = "echo " + select(select_cmd + {"//conditions:default": "default"})
        + " > $@"
  )

$ cat myapp/BUILD
selecty_genrule(
    name = "selecty",
    select_cmd = {
        "//tools/target_cpu:x86": "x86 mode",
    },
)

$ bazel build //testapp:selecty --cpu=x86 && cat bazel-genfiles/testapp/selecty.out
x86 mode WITH SUFFIX

select() 및 네이티브 유형을 모두 지원하려면 다음을 따르세요.

$ cat myapp/defs.bzl
def selecty_genrule(name, select_cmd):
    cmd_suffix = ""
    if type(select_cmd) == "string":
        cmd_suffix = select_cmd + " WITH SUFFIX"
    elif type(select_cmd) == "dict":
        for key in select_cmd.keys():
            select_cmd[key] += " WITH SUFFIX"
        cmd_suffix = select(select_cmd + {"//conditions:default": "default"})

    native.genrule(
        name = name,
        outs = [name + ".out"],
        srcs = [],
        cmd = "echo " + cmd_suffix + "> $@",
    )

select()가 bind()와 호환되지 않는 이유는 무엇인가요?

bind()은 BUILD 규칙이 아닌 WORKSPACE 규칙이므로

작업공간 규칙에는 특정 구성이 없으며, BUILD 규칙과 동일한 방식으로 평가되지 않습니다. 따라서 bind()select()는 실제로 특정 브랜치로 평가할 수 없습니다.

대신 이러한 유형의 런타임 결정을 실행하려면 actual 속성에 select()가 있는 alias()을 사용해야 합니다. alias()가 BUILD 규칙이며 특정 구성으로 평가되므로 이 방식은 올바르게 작동합니다.

필요한 경우 bind() 대상이 alias()을 가리키도록 할 수도 있습니다.

$ cat WORKSPACE
workspace(name = "myapp")
bind(name = "openssl", actual = "//:ssl")
http_archive(name = "alternative", ...)
http_archive(name = "boringssl", ...)

$ cat BUILD
config_setting(
    name = "alt_ssl",
    define_values = {
        "ssl_library": "alternative",
    },
)

alias(
    name = "ssl",
    actual = select({
        "//:alt_ssl": "@alternative//:ssl",
        "//conditions:default": "@boringssl//:ssl",
    }),
)

이 설정으로 --define ssl_library=alternative을 전달할 수 있으며 //:ssl 또는 //external:ssl에 종속되는 모든 대상은 @alternative//:ssl에 있는 대체 값을 볼 수 있습니다.

내 select()가 예상한 내용을 선택하지 않는 이유는 무엇인가요?

//myapp:foo에 예상한 조건을 선택하지 않는 select()이 있으면 cquerybazel config을 사용하여 디버깅합니다.

빌드 중인 최상위 대상이 //myapp:foo이면 다음을 실행합니다.

$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)

하위 그래프 어딘가에 //myapp:foo에 종속되는 다른 대상 //bar를 빌드하는 경우 다음을 실행합니다.

$ bazel cquery 'somepath(//bar, //myapp:foo)' <desired build flags>
//bar:bar   (3ag3193fee94a2)
//bar:intermediate_dep (12e23b9a2b534a)
//myapp:foo (12e23b9a2b534a)

//myapp:foo 옆의 (12e23b9a2b534a)//myapp:fooselect()를 확인하는 구성의 해시입니다. bazel config를 사용하여 값을 검사할 수 있습니다.

$ bazel config 12e23b9a2b534a
BuildConfigurationValue 12e23b9a2b534a
Fragment com.google.devtools.build.lib.analysis.config.CoreOptions {
  cpu: darwin
  compilation_mode: fastbuild
  ...
}
Fragment com.google.devtools.build.lib.rules.cpp.CppOptions {
  linkopt: [-Dfoo=bar]
  ...
}
...

그런 다음 이 출력을 각 config_setting에서 예상하는 설정과 비교합니다.

//myapp:foo은 같은 빌드의 다른 구성에 있을 수 있습니다. somepath를 사용하여 올바른 쿼리를 생성하는 방법에 대한 안내는 cquery 문서를 참조하세요.

select()가 플랫폼에서 작동하지 않는 이유는 무엇인가요?

시맨틱이 명확하지 않기 때문에 Bazel은 특정 플랫폼이 타겟 플랫폼인지 확인할 수 있는 구성 가능한 속성을 지원하지 않습니다.

예를 들면 다음과 같습니다.

platform(
    name = "x86_linux_platform",
    constraint_values = [
        "@platforms//cpu:x86",
        "@platforms//os:linux",
    ],
)

cc_library(
    name = "lib",
    srcs = [...],
    linkopts = select({
        ":x86_linux_platform": ["--enable_x86_optimizations"],
        "//conditions:default": [],
    }),
)

BUILD 파일에서 타겟 플랫폼에 @platforms//cpu:x86@platforms//os:linux 제약 조건이 모두 있지만 여기에 정의된 :x86_linux_platform아닌 경우 어떤 select()를 사용해야 하나요? BUILD 파일의 작성자와 별도의 플랫폼을 정의한 사용자는 서로 다른 아이디어를 가질 수 있습니다.

어떻게 해야 하나요?

대신 다음 제약 조건이 있는 모든 플랫폼과 일치하는 config_setting를 정의합니다.

config_setting(
    name = "is_x86_linux",
    constraint_values = [
        "@platforms//cpu:x86",
        "@platforms//os:linux",
    ],
)

cc_library(
    name = "lib",
    srcs = [...],
    linkopts = select({
        ":is_x86_linux": ["--enable_x86_optimizations"],
        "//conditions:default": [],
    }),
)

이 프로세스는 특정 시맨틱스를 정의하여 원하는 플랫폼을 충족하는 플랫폼을 사용자에게 더 명확하게 알려줍니다.

정말로 플랫폼에서 select하려면 어떻게 해야 할까요?

빌드 요구사항에서 특별히 플랫폼을 확인해야 하는 경우 config_setting에서 --platforms 플래그 값을 뒤집을 수 있습니다.

config_setting(
    name = "is_specific_x86_linux_platform",
    values = {
        "platforms": ["//package:x86_linux_platform"],
    },
)

cc_library(
    name = "lib",
    srcs = [...],
    linkopts = select({
        ":is_specific_x86_linux_platform": ["--enable_x86_optimizations"],
        "//conditions:default": [],
    }),
)

Bazel팀은 이러한 작업을 권장하지 않으며, 빌드를 과도하게 제한하고 예상 조건이 일치하지 않으면 사용자를 혼란스럽게 합니다.