Các thuộc tính có thể định cấu hình, thường được gọi là select()
, là một tính năng của Bazel cho phép người dùng bật/tắt các giá trị của thuộc tính quy tắc xây dựng tại dòng lệnh.
Ví dụ: Bạn có thể sử dụng lệnh này cho thư viện đa nền tảng tự động chọn phương thức triển khai phù hợp cho cấu trúc hoặc cho tệp nhị phân có thể định cấu hình tính năng có thể được tuỳ chỉnh tại thời điểm xây dựng.
Ví dụ:
# 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",
},
)
Thao tác này sẽ khai báo cc_binary
"chọn" phần phụ thuộc dựa trên cờ tại dòng lệnh. Cụ thể, deps
sẽ trở thành:
Lệnh | Phần phụ thuộc = |
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()
đóng vai trò là phần giữ chỗ cho một giá trị sẽ được chọn dựa trên các điều kiện cấu hình, là các nhãn tham chiếu đến mục tiêu config_setting
. Bằng cách sử dụng select()
trong một thuộc tính có thể định cấu hình, thuộc tính đó sử dụng các giá trị một cách hiệu quả khi lưu giữ nhiều điều kiện.
Kết quả trùng khớp phải rõ ràng: nếu nhiều điều kiện khớp, thì cả hai sẽ kết hợp với nhau thành một giá trị. Ví dụ: khi chạy trên linux x86, điều này là không rõ ràng
{"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}
vì cả hai nhánh đều phân giải thành "hello".
* values
của một người là siêu tập hợp nghiêm ngặt của tất cả những người khác. Ví dụ: values = {"cpu": "x86", "compilation_mode": "dbg"}
là chuyên môn không rõ ràng của values = {"cpu": "x86"}
.
Điều kiện tích hợp //conditions:default
sẽ tự động khớp khi không có điều kiện nào khác.
Mặc dù ví dụ này sử dụng deps
, nhưng select()
cũng hoạt động tốt trên srcs
,
resources
, cmd
và hầu hết các thuộc tính khác. Chỉ một số ít thuộc tính không thể định cấu hình, và chúng được chú thích rõ ràng. Ví dụ:
thuộc tính values
của config_setting
không thể định cấu hình.
select()
và các phần phụ thuộc
Một số thuộc tính nhất định thay đổi thông số bản dựng cho tất cả các phần phụ thuộc bắc cầu thuộc một mục tiêu. Ví dụ: tools
của genrule
thay đổi --cpu
thành CPU của máy chạy Bazel (do quá trình biên dịch chéo có thể khác với CPU mà mục tiêu được xây dựng). Đây được gọi là
quá trình chuyển đổi cấu hình.
Cho
#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"],
}),
)
chạy
$ bazel build //myapp:my_genrule --cpu=arm
trên máy phát triển x86
sẽ liên kết bản dựng với g_arm.src
, tool1
và x86tool.cc
. Cả hai select
được đính kèm vào my_genrule
đều sử dụng các tham số bản dựng của my_genrule
, bao gồm cả --cpu=arm
. Thuộc tính tools
thay đổi --cpu
thành x86
cho tool1
và các phần phụ thuộc bắc cầu của thuộc tính đó. select
trên
tool1
sử dụng các thông số bản dựng của tool1
, bao gồm --cpu=x86
.
Các điều kiện về cấu hình
Mỗi khoá trong một thuộc tính có thể định cấu hình là một tham chiếu nhãn đến config_setting
hoặc constraint_value
.
config_setting
chỉ là tập hợp các
tuỳ chọn cài đặt cờ hiệu dòng lệnh dự kiến. Bằng cách đóng gói các mục tiêu này trong một mục tiêu, bạn có thể dễ dàng duy trì các điều kiện "chuẩn" mà người dùng có thể tham chiếu từ nhiều vị trí.
constraint_value
hỗ trợ hành vi đa nền tảng.
Cờ tích hợp
Các cờ như --cpu
được tích hợp vào Bazel: công cụ xây dựng hiểu
bản gốc cho tất cả các bản dựng trong tất cả dự án. Bạn có thể chỉ định các thuộc tính này bằng thuộc tính values
của config_setting
:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN
là tên cờ (không có --
, vì vậy "cpu"
thay vì "--cpu"
). valueN
là giá trị dự kiến cho cờ đó. :meaningful_condition_name
sẽ khớp nếu
mọi mục nhập trong values
khớp. Đơn đặt hàng không liên quan.
valueN
được phân tích cú pháp như thể nó được đặt trên dòng lệnh. Điều này có nghĩa là:
values = { "compilation_mode": "opt" }
khớp vớibazel build -c opt
values = { "force_pic": "true" }
khớp vớibazel build --force_pic=1
values = { "force_pic": "0" }
khớp vớibazel build --noforce_pic
config_setting
chỉ hỗ trợ cờ ảnh hưởng đến hành vi mục tiêu. Ví dụ: không được phép sử dụng --show_progress
vì việc này chỉ ảnh hưởng đến tiến trình của Bazel đối với người dùng. Mục tiêu không thể sử dụng cờ đó để tạo kết quả. Hệ thống không ghi lại chính xác tập hợp cờ được hỗ trợ. Trong thực tế, hầu hết các cờ được coi là "có ý nghĩa".
Cờ tuỳ chỉnh
Bạn có thể mô hình hoá các cờ riêng của dự án bằng Chế độ cài đặt bản dựng Starlark. Không giống như cờ tích hợp sẵn, các cờ này được xác định là mục tiêu bản dựng, vì vậy, Bazel tham chiếu đến các nhãn này.
Những lệnh này được kích hoạt bằng thuộc tính flag_values
của config_setting
:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
Hành vi này giống như các cờ tích hợp sẵn. Xem ví dụ tại đây.
--define
là cú pháp cũ thay thế cho các cờ tuỳ chỉnh (ví dụ:
--define foo=bar
). Điều này có thể được biểu thị trong thuộc tính
giá trị
(values = {"define": "foo=bar"}
) hoặc
thuộc tính định_giá_trị (define_values = {"foo": "bar"}
). --define
chỉ được hỗ trợ cho khả năng tương thích ngược. Ưu tiên các chế độ cài đặt bản dựng Starlark bất cứ khi nào có thể.
values
, flag_values
và define_values
sẽ đánh giá độc lập. config_setting
sẽ khớp nếu tất cả giá trị trên tất cả đều khớp.
Điều kiện mặc định
Điều kiện tích hợp //conditions:default
sẽ khớp khi không có điều kiện nào khác khớp.
Do quy tắc "khớp chính xác một kết quả", một thuộc tính có thể định cấu hình không có kết quả trùng khớp và không có điều kiện mặc định sẽ gây ra lỗi "no matching conditions"
. Phương thức này có thể
bảo vệ bạn tránh được các lỗi im lặng trong phần cài đặt không mong muốn:
# 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
Để tránh các lỗi rõ ràng hơn, bạn có thể đặt thông báo tuỳ chỉnh bằng thuộc tính no_match_error
của select()
.
Các nền tảng
Mặc dù khả năng chỉ định nhiều cờ trên dòng lệnh mang lại sự linh hoạt, nhưng cũng có thể nặng nề khi đặt từng cờ mỗi lần bạn muốn tạo mục tiêu. Nền tảng cho phép bạn kết hợp các nền tảng này vào các gói đơn giản.
# 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",
],
)
Nền tảng có thể được chỉ định trên dòng lệnh. Thao tác này kích hoạt các config_setting
chứa một tập hợp con của constraint_values
của nền tảng, cho phép các config_setting
đó khớp với nhau trong biểu thức select()
.
Ví dụ: để đặt thuộc tính srcs
của my_rocks
thành calcite.sh
, bạn chỉ cần chạy
bazel build //my_app:my_rocks --platforms=//myapp:marble_platform
Nếu không có nền tảng, nội dung này có thể trông giống như
bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic
select()
cũng có thể đọc trực tiếp 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"],
}),
)
Điều này giúp tiết kiệm nhu cầu tạo mẫu config_setting
khi bạn chỉ cần kiểm tra các giá trị riêng lẻ.
Các nền tảng vẫn đang trong quá trình phát triển. Vui lòng xem tài liệu để biết thông tin chi tiết.
Kết hợp select()
select
có thể xuất hiện nhiều lần trong cùng một thuộc tính:
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
không thể xuất hiện bên trong một select
khác. Nếu bạn cần lồng selects
và thuộc tính của bạn lấy các mục tiêu khác làm giá trị, hãy sử dụng mục tiêu trung gian:
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"],
...
}),
)
Nếu bạn cần một select
để khớp khi nhiều điều kiện khớp, hãy cân nhắc VÀ tạo chuỗi.
Chuỗi cửa hàng
Hãy cân nhắc thực hiện những bước sau:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1": [":standard_lib"],
":config2": [":standard_lib"],
":config3": [":standard_lib"],
":config4": [":special_lib"],
}),
)
Hầu hết các điều kiện đều có cùng một phần phụ thuộc. Nhưng cú pháp này rất khó đọc và
duy trì. Thật tuyệt khi không phải lặp lại [":standard_lib"]
nhiều lần.
Một tuỳ chọn là xác định trước giá trị dưới dạng biến 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"],
}),
)
Việc này giúp bạn dễ dàng quản lý phần phụ thuộc. Nhưng điều đó vẫn gây ra sự trùng lặp không cần thiết.
Để được hỗ trợ trực tiếp hơn, hãy sử dụng một trong các công cụ sau:
selects.with_or
Macro with_or trong mô-đun selects
của Skylib hỗ trợ OR
các điều kiện trực tiếp bên trong một select
:
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
Macro
config_setting_group
trong mô-đun selects
Skylib hỗ trợ OR
thêm nhiều config_setting
:
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"],
}),
)
Không giống như selects.with_or
, các mục tiêu khác nhau có thể chia sẻ :config1_or_2
trên
các thuộc tính khác nhau.
Đây là lỗi khi nhiều điều kiện cần khớp nhau trừ khi một điều kiện là "đặc biệt" không rõ ràng của các điều kiện khác hoặc tất cả đều phân giải cùng một giá trị. Để biết thông tin chi tiết, hãy xem tại đây.
VÀ chuỗi
Nếu bạn cần một nhánh select
khớp với khi nhiều điều kiện khớp, hãy sử dụng macro 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"],
}),
)
Không giống như chuỗi chuỗi OR, các config_setting
hiện có không thể AND
trực tiếp bên trong select
. Bạn phải gói gọn các thông báo này trong config_setting_group
.
Thông báo lỗi tuỳ chỉnh
Theo mặc định, khi không có điều kiện nào phù hợp, mục tiêu mà select()
được đính kèm sẽ không thành công kèm theo lỗi:
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
Bạn có thể tuỳ chỉnh thuộc tính này bằng thuộc tính 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
Khả năng tương thích với quy tắc
Việc triển khai quy tắc sẽ nhận được giá trị đã giải quyết của các thuộc tính có thể định cấu hình. Ví dụ như:
# myapp/BUILD
some_rule(
name = "my_target",
some_attr = select({
":foo_mode": [":foo"],
":bar_mode": [":bar"],
}),
)
$ bazel build //myapp/my_target --define mode=foo
Mã triển khai quy tắc sẽ thấy ctx.attr.some_attr
là [":foo"]
.
Macro có thể chấp nhận mệnh đề select()
và chuyển chúng vào các quy tắc gốc. Tuy nhiên, chúng không thể trực tiếp thao tác với chúng. Ví dụ: không có cách nào để macro chuyển đổi
select({"foo": "val"}, ...)
tới
select({"foo": "val_with_suffix"}, ...)
Điều này là vì hai lý do.
Trước tiên, các macro cần biết đường dẫn nào select
sẽ chọn không thể hoạt động
vì macro được đánh giá trong giai đoạn tải của Bazel,
xảy ra trước khi giá trị cờ được biết đến.
Đây là một hạn chế về thiết kế cốt lõi của Bazel và ít có khả năng thay đổi sớm.
Thứ hai, các macro chỉ cần lặp lại tất cả các đường dẫn select
, trong khi về mặt kỹ thuật thì không có giao diện người dùng nhất quán. Bạn cần thiết kế thêm để thay đổi
điều này.
Truy vấn Bazel và truy vấn
Bazel query
hoạt động trong giai đoạn tải của Bazel.
Điều này có nghĩa là hệ thống không biết cờ mà mục tiêu sử dụng vì những cờ đó sẽ không được đánh giá cho đến khi có bản dựng khác (trong giai đoạn phân tích).
Vì vậy, hệ thống không thể xác định nhánh select()
nào được chọn.
Bazel cquery
hoạt động sau giai đoạn phân tích của Bazel, vì vậy, Bazel có tất cả thông tin này và có thể phân giải chính xác select()
.
Hãy cân nhắc:
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
ước tính cao hơn các phần phụ thuộc của :my_lib
:
$ bazel query 'deps(//myapp:my_lib)'
//myapp:my_lib
//myapp:foo_dep
//myapp:bar_dep
trong khi cquery
hiển thị các phần phụ thuộc chính xác của nó:
$ bazel cquery 'deps(//myapp:my_lib)' --//myapp:dog_type=pug
//myapp:my_lib
//myapp:bar_dep
Câu hỏi thường gặp
Tại sao Select() không hoạt động trong macro?
Select() có hoạt động trong quy tắc! Vui lòng xem nội dung Khả năng tương thích của quy tắc để biết thông tin chi tiết.
Vấn đề chính mà câu hỏi này thường có nghĩa là Select() không hoạt động trong macro. Các quy tắc này khác với các quy tắc. Vui lòng xem tài liệu về quy tắc và macro để hiểu sự khác biệt. Sau đây là ví dụ toàn diện:
Xác định quy tắc và macro:
# 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)
Tạo quy tắc và macro:
# 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",
}),
)
Không thể tạo vì sad_macro
không thể xử lý 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.
Xây dựng thành công khi bạn nhận xét 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.
Bạn không thể thay đổi macro này vì macro theo định nghĩa được đánh giá trước khi Bazel đọc cờ dòng lệnh của bản dựng. Điều đó có nghĩa là không có đủ thông tin để đánh giá các hàm select().
Tuy nhiên, macro có thể chuyển select()
dưới dạng blob mờ đến các quy tắc:
# 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.
Tại sao Select() luôn trả về true?
Vì macro (nhưng không phải các quy tắc) theo định nghĩa
không thể đánh giá select()
s nên thường cố gắng thực hiện lỗi:
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().
Boolean là một trường hợp đặc biệt không thành công mà không biết tiếng. Vì vậy, bạn nên thận trọng với chúng:
$ 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.
Điều này xảy ra vì macro không hiểu nội dung của select()
.
Vì vậy, những gì họ thực sự đánh giá là chính đối tượng select()
. Theo tiêu chuẩn thiết kế
Pythonic, tất cả đối tượng ngoài số lượng ngoại lệ rất nhỏ
tự động trả về giá trị true.
Tôi có thể đọc Select() như một dict không?
Macro không thể đánh giá(các) lựa chọn vì macro đánh giá trước khi Bazel biết tham số dòng lệnh của bản dựng là gì. Họ có thể đọc ít nhất là trong từ điển của select()
chẳng hạn để thêm hậu tố vào mỗi giá trị không?
Về mặt lý thuyết, điều này có thể thực hiện được, nhưng vẫn chưa phải là tính năng của Bazel.
Việc bạn có thể làm hôm nay là chuẩn bị từ điển thẳng, sau đó cung cấp từ điển vào 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
Nếu muốn hỗ trợ cả loại select()
và loại gốc, bạn có thể làm việc này:
$ 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 + "> $@",
)
Tại sao Select() không hoạt động với binding()?
Vì bind()
là quy tắc WORKSPACE, không phải quy tắc BUILD.
Các quy tắc của Workspace không có cấu hình cụ thể và không được đánh giá theo cách giống như các quy tắc XÂY DỰNG. Do đó, select()
trong bind()
thực sự không thể
đánh giá bất kỳ nhánh cụ thể nào.
Thay vào đó, bạn nên sử dụng alias()
với select()
trong thuộc tính actual
để thực hiện loại xác định thời gian chạy này. Điều này hoạt động chính xác, vì alias()
là quy tắc BUILD và được đánh giá bằng một cấu hình cụ thể.
Bạn thậm chí có thể nhắm mục tiêu bind()
đến alias()
, nếu cần.
$ 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",
}),
)
Với cách thiết lập này, bạn có thể chuyển --define ssl_library=alternative
và bất kỳ mục tiêu nào
phụ thuộc vào //:ssl
hoặc //external:ssl
đều sẽ thấy phương án thay thế
được đặt tại @alternative//:ssl
.
Tại sao select() không chọn điều tôi mong đợi?
Nếu //myapp:foo
có một select()
không chọn điều kiện bạn mong đợi,
hãy dùng cquery và bazel config
để gỡ lỗi:
Nếu //myapp:foo
là mục tiêu cấp cao nhất mà bạn đang tạo, hãy chạy:
$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)
Nếu bạn đang tạo một số mục tiêu //bar
khác phụ thuộc vào
//myapp:foo ở một nơi nào đó trong biểu đồ con, hãy chạy:
$ bazel cquery 'somepath(//bar, //myapp:foo)' <desired build flags>
//bar:bar (3ag3193fee94a2)
//bar:intermediate_dep (12e23b9a2b534a)
//myapp:foo (12e23b9a2b534a)
(12e23b9a2b534a)
bên cạnh //myapp:foo
là một hash của cấu hình phân giải select()
của //myapp:foo
. Bạn có thể kiểm tra các giá trị của bazel config
bằng:
$ 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]
...
}
...
Sau đó, so sánh kết quả này với chế độ cài đặt mà mỗi config_setting
dự kiến.
//myapp:foo
có thể tồn tại ở các cấu hình khác nhau trong cùng một bản dựng. Vui lòng xem tài liệu về truy vấn để biết hướng dẫn sử dụng somepath
nhằm tải đúng tài liệu.
Tại sao select()
không hoạt động với các nền tảng?
Bazel không hỗ trợ các thuộc tính có thể định cấu hình để kiểm tra xem một nền tảng nhất định có phải là nền tảng mục tiêu hay không vì ngữ nghĩa không rõ ràng.
Ví dụ:
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": [],
}),
)
Trong tệp BUILD
này, bạn nên sử dụng select()
nào nếu nền tảng mục tiêu có cả ràng buộc @platforms//cpu:x86
và @platforms//os:linux
, nhưng không phải là :x86_linux_platform
được xác định ở đây? Tác giả của tệp BUILD
và người dùng xác định nền tảng riêng biệt có thể có các ý tưởng khác nhau.
Tôi nên làm gì thay thế?
Thay vào đó, hãy xác định config_setting
phù hợp với bất kỳ nền tảng nào có các quy tắc ràng buộc sau:
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": [],
}),
)
Quá trình này xác định các ngữ nghĩa cụ thể, giúp người dùng biết rõ hơn về những nền tảng đáp ứng các điều kiện mong muốn.
Nếu tôi thực sự muốn select
trên nền tảng thì sao?
Nếu các yêu cầu về bản dựng cụ thể yêu cầu kiểm tra nền tảng, bạn có thể lật giá trị của cờ --platforms
trong config_setting
:
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": [],
}),
)
Nhóm Bazel không chứng thực việc này; việc này ràng buộc quá mức bản dựng của bạn và gây nhầm lẫn cho người dùng khi điều kiện dự kiến không khớp.