구성 가능한 속성(일반적으로 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",
},
)
이렇게 하면 명령줄의 플래그를 기반으로 종속 항목을 '선택'하는 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
는 모두 --cpu=arm
가 포함된 my_genrule
의 빌드 매개변수를 사용합니다. 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 속성(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_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"],
}),
)
대부분의 조건은 동일한 종속 항목으로 평가됩니다. 하지만 이 문법은 읽고 유지하기가 어렵습니다. [":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
ing 조건을 지원합니다.
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({
"//third_party/bazel_platforms/cpu:x86_32": "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({
"//third_party/bazel_platforms/cpu:x86_32": "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({
"//third_party/bazel_platforms/cpu:x86_32": 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()
객체 자체입니다. Pythonic 디자인 표준에 따라 소수의 예외를 제외한 모든 객체는 자동으로 true를 반환합니다.
select()를 dict처럼 읽을 수 있나요?
매크로가 select(s)를 평가할 수 없습니다. 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 = {
"//third_party/bazel_platforms/cpu:x86_32": "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()
를 사용하지 마세요. alias()
로 대체되었습니다.
기술적 답변은 bind()
가 BUILD 규칙이 아닌 저장소 규칙이라는 것입니다.
저장소 규칙에는 특정 구성이 없으며 BUILD 규칙과 동일한 방식으로 평가되지 않습니다. 따라서 bind()
의 select()
는 실제로 특정 브랜치로 평가될 수 없습니다.
대신 actual
속성에 select()
가 있는 alias()
를 사용하여 이러한 유형의 런타임 결정을 실행해야 합니다. alias()
는 BUILD 규칙이며 특정 구성으로 평가되므로 올바르게 작동합니다.
$ 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
에 있는 대체 항목이 표시됩니다.
하지만 bind()
사용을 중지해야 합니다.
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팀은 이 작업을 권장하지 않습니다. 이는 빌드를 과도하게 제한하고 예상 조건이 일치하지 않으면 사용자에게 혼란을 줍니다.