Thuộc tính có thể định cấu hình, thường gọi là select()
, là một tính năng của Bazel cho phép người dùng chuyển đổi giữa các giá trị
của các thuộc tính quy tắc tạo ở dòng lệnh.
Ví dụ: bạn có thể sử dụng tính năng này cho một thư viện đa nền tảng tự động chọn cách triển khai phù hợp cho cấu trúc hoặc cho một 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
"lựa chọn" dựa trên các cờ ở
dòng lệnh. Cụ thể, deps
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
điều kiện cấu hình, là các nhãn tham chiếu đến config_setting
mục tiêu. Bằng cách sử dụng select()
trong thuộc tính có thể định cấu hình, thuộc tính này
áp dụng hiệu quả các giá trị khác nhau khi các điều kiện khác nhau có hiệu lực.
Kết quả trùng khớp phải rõ ràng: nếu nhiều điều kiện khớp thì
* Tất cả các giá trị này đều đưa ra cùng một giá trị. Ví dụ: khi chạy trên linux x86, điều này là 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 là tập mẹ nghiêm ngặt của tất cả các tập khác. Ví dụ: values = {"cpu": "x86", "compilation_mode": "dbg"}
là một chuyên môn rõ ràng của values = {"cpu": "x86"}
.
Điều kiện tích hợp sẵn //conditions:default
sẽ tự động khớp khi
không có tác dụng nào khác.
Mặc dù ví dụ này sử dụng deps
, nhưng select()
cũng hoạt động 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à các chế độ cài đặt này được chú thích rõ ràng. Ví dụ:
Chính sách của config_setting
Thuộc tính values
không thể định cấu hình.
select()
và phần phụ thuộc
Một số thuộc tính thay đổi tham số bản dựng cho tất cả các phần phụ thuộc bắc cầu
theo một mục tiêu. Ví dụ: tools
của genrule
thay đổi --cpu
thành CPU của
cỗ máy chạy Bazel (nhờ quá trình biên dịch chéo, có thể khác
CPU mà mục tiêu được xây dựng). Đây được gọi là
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"],
}),
)
đang chạy
$ bazel build //myapp:my_genrule --cpu=arm
trên máy của nhà 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 my_genrule
các tham số bản dựng, bao gồm --cpu=arm
. Thuộc tính tools
thay đổi
--cpu
đến x86
cho tool1
và các phần phụ thuộc bắc cầu của nó. select
bật
tool1
sử dụng các tham số bản dựng của tool1
, bao gồm cả --cpu=x86
.
Các điều kiện về cấu hình
Mỗi khoá trong thuộc tính có thể định cấu hình là một tham chiếu nhãn đến một
config_setting
hoặc
constraint_value
.
config_setting
chỉ là một bộ sưu tập
cài đặt cờ dòng lệnh dự kiến. Bằng cách gộp những thông tin này lại trong một mục tiêu,
dễ duy trì "tiêu chuẩn" mà người dùng có thể tham khảo từ nhiều nơi.
constraint_value
hỗ trợ hành vi trên nhiều 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 này hiểu rõ nguyên bản
cho tất cả các bản dựng trong mọi dự án. Các tham số này được chỉ định bằng
Điều khoản dịch vụ và Chính sách quyền riêng tư của config_setting
Thuộc tính values
:
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
khớp nếu
mọi mục nhập trong values
khớp. Thứ tự không liên quan.
valueN
được phân tích cú pháp như thể được đặt trên dòng lệnh. Điều này có nghĩa là:
values = { "compilation_mode": "opt" }
đối sánhbazel build -c opt
values = { "force_pic": "true" }
đối sánhbazel build --force_pic=1
values = { "force_pic": "0" }
đối sánhbazel build --noforce_pic
config_setting
chỉ hỗ trợ những cờ ảnh hưởng đến hành vi của mục tiêu. Ví dụ:
Không được phép sử dụng --show_progress
vì
nó chỉ ảnh hưởng đến cách Bazel báo cáo tiến trình cho người dùng. Các mục tiêu không được sử dụng thông tin đó
gắn cờ để tạo kết quả của chúng. Tập hợp chính xác các cờ được hỗ trợ không
được ghi lại. Trong thực tế, hầu hết những cờ "có ý nghĩa" cơ quan.
Cờ tuỳ chỉnh
Bạn có thể lập mô hình các cờ dành riêng cho dự án của riêng mình bằng Chế độ cài đặt bản dựng Starlark. Không giống như cờ tích hợp, đây là được xác định là mục tiêu bản dựng, vì vậy Bazel tham chiếu chúng bằng nhãn mục tiêu.
Các lượt chuyển đổi này được kích hoạt bằng thông số của config_setting
flag_values
thuộc tính:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
Cách hoạt động tương tự như đối với cờ tích hợp sẵn. Xem tại đây để có ví dụ hiệu quả.
--define
là một cú pháp cũ thay thế cho cờ tuỳ chỉnh (ví dụ:
--define foo=bar
). Điều này có thể được thể hiện trong
thuộc tính values
(values = {"define": "foo=bar"}
) hoặc
Thuộc tính define_values
(define_values = {"foo": "bar"}
). --define
chỉ được hỗ trợ cho thao tác quay lại
khả năng tương thích. Ưu tiên 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á một cách độc lập. Chiến lược phát hành đĩa đơn
config_setting
khớp nếu tất cả giá trị trên tất cả các giá trị đó trùng khớp.
Điều kiện mặc định
Điều kiện tích hợp //conditions:default
khớp khi không có điều kiện nào khác
kết quả phù hợp.
Vì "đúng một kết quả trùng khớp" quy tắc, thuộc tính có thể định cấu hình không có kết quả phù hợp
và không có điều kiện mặc định nào tạo ra lỗi "no matching conditions"
. Điều này có thể
bảo vệ chống lại lỗi ẩn từ các 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
Để rõ ràng hơn nữa về lỗi, bạn có thể đặt thông báo tuỳ chỉnh bằngselect()
no_match_error
.
Nền tảng
Mặc dù khả năng chỉ định nhiều cờ trên dòng lệnh cung cấp linh hoạt, nhưng việc đặt riêng từng cấu hình mỗi lần cũng có thể khó khăn mà bạn muốn xây dựng một mục tiêu. Nền tảng để bạn gộp chúng thành 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",
],
)
Bạn có thể chỉ định nền tảng qua dòng lệnh. Việc này sẽ kích hoạt
config_setting
chứa một tập hợp con constraint_values
của nền tảng,
cho phép các config_setting
đó khớp trong biểu thức select()
.
Ví dụ: để đặt thuộc tính srcs
của my_rocks
thành calcite.sh
,
bạn có thể chỉ cần chạy
bazel build //my_app:my_rocks --platforms=//myapp:marble_platform
Nếu không có nền tảng, thì URL 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ể trực tiếp đọc các 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"],
}),
)
Việc này sẽ giúp bạn không cần phải có các config_setting
nguyên mẫu khi chỉ cần
hãy kiểm tra các giá trị đơn lẻ.
Các nền tảng vẫn đang trong quá trình phát triển. 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
còn thuộc tính của bạn lấy 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 select
để khớp khi có nhiều điều kiện khớp, hãy cân nhắc sử dụng phương thức AND
tạo chuỗi.
Tạo chuỗi OR
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 được đánh giá theo cùng một phần phụ thuộc. Tuy nhiên, cú pháp này khó đọc và
duy trì. Sẽ tốt hơn nếu bạn không phải lặp lại [":standard_lib"]
nhiều
lần.
Bạn có thể 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"],
}),
)
Thao tác này giúp bạn quản lý phần phụ thuộc dễ dàng hơn. Nhưng nó vẫn dẫn đến việc không cần thiết trùng lặp.
Để được hỗ trợ trực tiếp hơn, vui lòng sử dụng một trong các cách sau:
selects.with_or
Chiến lược phát hành đĩa đơn
with_or
macro trong tệp của Skylib
selects
mô-đun hỗ trợ OR
ing các điều kiện trực tiếp bên trong 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
Chiến lược phát hành đĩa đơn
config_setting_group
macro trong tệp của Skylib
selects
mô-đun hỗ trợ OR
ing 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.
Sẽ có lỗi khi nhiều điều kiện khớp, trừ phi một điều kiện rõ ràng "chuyên môn" của những giá trị khác hoặc tất cả đều phân giải đến cùng một giá trị. Xem tại đây để biết chi tiết.
VÀ tạo chuỗi
Nếu bạn cần một nhánh select
để khớp khi có 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ư cách tạo chuỗi OR, bạn không thể AND
trực tiếp các config_setting
hiện có
bên trong một select
. Bạn phải gói chúng một cách rõ ràng trong một 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()
sẽ được đính kèm
không thực hiện được lỗi này:
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 tính năng này bằng no_match_error
thuộc tính:
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
Các quá trình triển khai quy tắc nhận giá trị đã phân giải của quy tắc có thể định cấu hình . Ví dụ: ví dụ:
# 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 thấy ctx.attr.some_attr
là [":foo"]
.
Macro có thể chấp nhận mệnh đề select()
và chuyển chúng đến các mệnh đề gốc
quy tắc. Tuy nhiên, họ không thể trực tiếp thao túng các hoạt độ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 mà 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 xác định được các giá trị gắn cờ.
Đây là một hạn chế chính trong thiết kế của Bazel và có thể sẽ sớm thay đổi.
Thứ hai, các macro chỉ cần lặp lại trên tất cả đường dẫn select
, trong khi
khả thi về mặt kỹ thuật, thiếu giao diện người dùng mạch lạc. Cần thiết kế thêm để thay đổi
này.
Truy vấn Bazel và cquery
Bazel query
hoạt động trên khu vực của Bazel
giai đoạn tải.
Tức là nó không biết dòng lệnh nào gắn cờ mục tiêu vì những
cờ không được đánh giá cho đến sau này trong bản dựng (trong
giai đoạn phân tích).
Vì vậy, công cụ này 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, do đó,
tất cả thông tin này và có thể giải quyết select()
một cách chính xác.
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 lượng quá mức 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
cho thấy 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! Xem phần Khả năng tương thích với quy tắc để 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. Những quy tắc này khác với quy tắc. Xem tài liệu về quy tắc và macro để hiểu sự khác biệt. Sau đây là ví dụ từ đầu đến cuối:
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 thực thể cho 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({
"//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",
}),
)
Không tạo được 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.
Quá trình tạo sẽ thành công khi bạn nhận xét về 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 chế độ này vì macro theo định nghĩa được đánh giá trước Bazel đọc cờ dòng lệnh của bản dựng. Điều đó có nghĩa là không có đủ để đánh giá các hàm Select().
Tuy nhiên, macro có thể truyề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ề giá trị true?
Vì theo định nghĩa thì macro (chứ không phải quy tắc)
không thể đánh giá select()
, bất kỳ nỗ lực nào để làm như vậy
thường tạo ra 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ột cách âm thầm, do đó bạn nên đặc biệt cảnh giác với họ:
$ 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.
Điều này xảy ra vì macro không hiểu nội dung của select()
.
Vì vậy, điều họ thực sự đánh giá là chính đối tượng select()
. Theo
Thiết kế Pythonic
tất cả các đối tượng trừ một số rất ít ngoại lệ
tự động trả về giá trị true.
Tôi có thể đọc Select() như một lệnh chính tả không?
Macro không thể đánh giá(các) lựa chọn vì macro đánh giá trước
Bazel biết các tham số dòng lệnh của bản dựng là gì. Ít nhất thì họ có đọc được
từ điển của select()
chẳng hạn để thêm hậu tố cho mỗi giá trị?
Về mặt lý thuyết thì tính năng này là có thể thực hiện được, nhưng tính năng này 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ị một từ điển trực tiếp, sau đó đưa 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 = {
"//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
Nếu muốn hỗ trợ cả select()
và kiểu gốc, thì bạn có thể làm như sau:
$ 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()?
Trước hết, không sử dụng bind()
. Ngừng sử dụng và thay bằng alias()
.
Câu trả lời về mặt kỹ thuật là bind()
là một kho lưu trữ
chứ không phải quy tắc BUILD.
Quy tắc repo không có cấu hình cụ thể và không được đánh giá trong
giống như quy tắc XÂY DỰNG. Do đó, select()
trong bind()
không thể
đánh giá thực tế cho 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. Chiến dịch này
hoạt động đúng cách vì alias()
là quy tắc BUILD và được đánh giá bằng
cấu hình cụ thể.
Bạn thậm chí có thể dùng điể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ể truyề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
sẽ thấy phương án thay thế
đặt tại @alternative//:ssl
.
Tuy nhiên, hãy ngừng sử dụng bind()
.
Tại sao Select() của tôi không chọn những gì tôi mong đợi?
Nếu //myapp:foo
có select()
không chọn điều kiện bạn mong đợi,
sử 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ố //bar
mục tiêu khác phụ thuộc vào
//myapp:foo ở đâu đó trong đồ thị con của nó, 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 hàm băm của
để phân giải select()
của //myapp:foo
. Bạn có thể kiểm tra
các giá trị bằng 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]
...
}
...
Sau đó, hãy so sánh kết quả này với chế độ cài đặt dự kiến của mỗi config_setting
.
//myapp:foo
có thể tồn tại ở nhiều cấu hình trong cùng một bản dựng. Xem
tài liệu về cquery để được hướng dẫn về cách sử dụng somepath
nhằm đưa ra
một.
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 cụ thể có được hỗ trợ hay không là nền tảng mục tiêu 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 dùng select()
nếu nền tảng mục tiêu có cả
Có hạn chế đối với @platforms//cpu:x86
và @platforms//os:linux
, nhưng không phải là
:x86_linux_platform
được xác định tại đây? Tác giả của tệp BUILD
và người dùng
người 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 vào đó, hãy xác định config_setting
khớp với bất kỳ nền tảng nào có
những hạn chế 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": [],
}),
)
Quy trình này xác định các ngữ nghĩa cụ thể, giúp người dùng hiểu rõ hơn những gì 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 này thì sao?
Nếu các yêu cầu về bản dựng của bạn đòi hỏi cụ thể việc 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 tán thành việc này; nó ràng buộc quá mức đến bản dựng của bạn và sẽ làm người dùng nhầm lẫn khi điều kiện dự kiến không khớp.