일반적으로 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()
를 사용하면 서로 다른 조건이 적용될 때 속성에서 다른 값을 효과적으로 적용합니다.
일치는 명확해야 합니다. 여러 조건이 일치하면 다음 중 하나여야 합니다.
* 모두 동일한 값으로 결정됩니다. 예를 들어 Linux x86에서 실행하는 경우 두 브랜치가 모두 'hello'로 확인되므로 모호하지 않은 {"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}
입니다.
* 특정 요소의 values
는 다른 모든 요소의 엄격한 상위 집합입니다. 예를 들어 values = {"cpu": "x86", "compilation_mode": "dbg"}
는 values = {"cpu": "x86"}
의 확실한 전문 분야입니다.
기본 제공 조건 //conditions:default
은 다른 조건이 없을 때 자동으로 일치합니다.
이 예에서는 deps
를 사용하지만 select()
는 srcs
, resources
, cmd
및 대부분의 다른 속성에서도 잘 작동합니다. 소수의 속성만 구성 불가능하며 명확하게 주석이 달립니다. 예를 들어 config_setting
의 자체 values
속성은 구성할 수 없습니다.
select()
및 종속 항목
특정 속성은 타겟 아래의 모든 전이 종속 항목에 관한 빌드 매개변수를 변경합니다. 예를 들어 genrule
의 tools
는 --cpu
를 Bazel을 실행하는 머신의 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
및 전이 종속 항목의 --cpu
를 x86
로 변경합니다. tool1
의 select
는 --cpu=x86
가 포함된 tool1
의 빌드 매개변수를 사용합니다.
구성 조건
구성 가능한 속성의 각 키는 config_setting
또는 constraint_value
의 라벨 참조입니다.
config_setting
은 예상 명령줄 플래그 설정의 모음입니다. 이를 타겟에 캡슐화하면 사용자가 여러 위치에서 참조할 수 있는 '표준' 조건을 쉽게 유지할 수 있습니다.
constraint_value
는 멀티 플랫폼 동작을 지원합니다.
기본 제공 플래그
--cpu
와 같은 플래그는 Bazel에 내장되어 있습니다. 빌드 도구는 기본적으로 모든 프로젝트의 모든 빌드에 대해 플래그를 인식합니다. 이러한 항목은 config_setting
의 values
속성으로 지정됩니다.
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_setting
의 flag_values
속성으로 트리거됩니다.
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
동작은 기본 제공 플래그의 경우와 동일합니다. 작업 예는 여기를 참조하세요.
--define
는 맞춤 플래그의 기존 대체 구문입니다 (예: --define foo=bar
). 이는 값 속성(values = {"define": "foo=bar"}
) 또는 define_values 속성(define_values = {"foo": "bar"}
)으로 표현할 수 있습니다. --define
는 이전 버전과의 호환성에만 지원됩니다. 가능하면 Starlark 빌드 설정을 사용하는 것이 좋습니다.
values
, flag_values
, define_values
는 독립적으로 평가됩니다. 모든 값의 모든 값이 일치하면 config_setting
가 일치합니다.
기본 조건
내장된 조건 //conditions:default
는 일치하는 다른 조건이 없을 때 일치합니다.
'정확히 일치 항목 1개' 규칙으로 인해 일치 항목이 없고 기본 조건이 없는 구성 가능한 속성은 "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
를 활성화하여 config_setting
가 select()
표현식에서 일치하도록 합니다.
예를 들어 my_rocks
의 srcs
속성을 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
Skylib의 selects
모듈에 있는 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
Skylib의 selects
모듈에 있는 config_setting_group 매크로는 여러 config_setting
의 OR
를 지원합니다.
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_setting
는 select
내에서 직접 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"}, ...)
to
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",
"//third_party/bazel_platforms/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",
"//third_party/bazel_platforms/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,
"//third_party/bazel_platforms/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를 반환합니다.
dict처럼 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()
이 예상하는 조건을 선택하지 않으면 cquery 및 bazel 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:foo
의 select()
를 확인하는 구성의 해시입니다. 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팀은 이를 지지하지 않으며, 빌드를 과도하게 제한하고 예상 조건이 일치하지 않을 때 사용자를 혼란스럽게 합니다.