Trang này trình bày các lợi ích và cách sử dụng cơ bản của cấu hình Starlark, API của Bazel để tuỳ chỉnh cách dự án của bạn tạo bản dựng. Nó bao gồm cách xác định chế độ cài đặt bản dựng và cung cấp các ví dụ.
Điều này giúp bạn có thể:
- xác định cờ tuỳ chỉnh cho dự án của bạn, loại bỏ nhu cầu về
--define
- viết các quá trình chuyển đổi để định cấu hình các phần phụ thuộc trong các cấu hình khác với cấu hình của phần tử mẹ (chẳng hạn như
--compilation_mode=opt
hoặc--cpu=arm
) - tích hợp các giá trị mặc định tốt hơn vào quy tắc (chẳng hạn như tự động tạo
//my:android_app
bằng một SDK cụ thể)
và nhiều nội dung khác, tất cả đều hoàn toàn từ các tệp .bzl (không cần bản phát hành Bazel). Hãy xem kho lưu trữ bazelbuild/examples
để biết các ví dụ.
Chế độ cài đặt bản dựng do người dùng xác định
Chế độ cài đặt bản dựng là một phần thông tin cấu hình. Hãy coi cấu hình là một bản đồ khoá/giá trị. Việc đặt --cpu=ppc
và --copt="-DFoo"
sẽ tạo ra một cấu hình có dạng như {cpu: ppc, copt: "-DFoo"}
. Mỗi mục là một chế độ cài đặt bản dựng.
Các cờ truyền thống như cpu
và copt
là các chế độ cài đặt gốc – khoá của chúng được xác định và giá trị của chúng được đặt bên trong mã java bazel gốc.
Người dùng Bazel chỉ có thể đọc và ghi các tệp này thông qua dòng lệnh và các API khác được duy trì theo cách gốc. Việc thay đổi cờ gốc và các API hiển thị cờ đó đòi hỏi phải có một bản phát hành bazel. Chế độ cài đặt bản dựng do người dùng xác định được xác định trong các tệp .bzl
(do đó, không cần bản phát hành bazel để đăng ký các thay đổi). Bạn cũng có thể đặt các hiệu ứng này thông qua dòng lệnh (nếu được chỉ định là flags
, hãy xem thêm bên dưới), nhưng cũng có thể đặt thông qua hiệu ứng chuyển đổi do người dùng xác định.
Xác định chế độ cài đặt bản dựng
Tham số build_setting
rule()
Chế độ cài đặt bản dựng là các quy tắc giống như mọi quy tắc khác và được phân biệt bằng cách sử dụng build_setting
thuộc tính của hàm rule()
Starlark.
# example/buildsettings/build_settings.bzl
string_flag = rule(
implementation = _impl,
build_setting = config.string(flag = True)
)
Thuộc tính build_setting
lấy một hàm chỉ định loại chế độ cài đặt bản dựng. Loại này bị giới hạn trong một nhóm các loại Starlark cơ bản như bool
và string
. Hãy xem tài liệu về mô-đun config
để biết thông tin chi tiết. Bạn có thể thực hiện việc nhập phức tạp hơn trong hàm triển khai của quy tắc. Xem thêm về vấn đề này ở bên dưới.
Các hàm của mô-đun config
nhận một tham số boolean không bắt buộc, flag
, theo mặc định được đặt thành false. Nếu flag
được đặt thành true, thì người dùng có thể đặt chế độ cài đặt bản dựng trên dòng lệnh cũng như nội bộ bằng cách viết quy tắc thông qua các giá trị mặc định và quá trình chuyển đổi.
Không phải người dùng nào cũng có thể đặt tất cả chế độ cài đặt. Ví dụ: nếu là người viết quy tắc, bạn có một số chế độ gỡ lỗi mà bạn muốn bật trong các quy tắc kiểm thử, thì bạn không muốn cho phép người dùng bật tính năng đó một cách tuỳ ý trong các quy tắc không phải là quy tắc kiểm thử.
Sử dụng ctx.build_setting_value
Giống như mọi quy tắc khác, quy tắc cài đặt bản dựng có các hàm triển khai.
Bạn có thể truy cập vào giá trị cơ bản thuộc loại Starlark của chế độ cài đặt bản dựng thông qua phương thức ctx.build_setting_value
. Phương thức này chỉ dành cho các đối tượng ctx
của quy tắc cài đặt bản dựng. Các phương thức triển khai này có thể chuyển tiếp trực tiếp giá trị chế độ cài đặt bản dựng hoặc thực hiện thêm công việc trên giá trị đó, chẳng hạn như kiểm tra loại hoặc tạo cấu trúc phức tạp hơn. Sau đây là cách bạn triển khai chế độ cài đặt bản dựng có kiểu enum
:
# example/buildsettings/build_settings.bzl
TemperatureProvider = provider(fields = ['type'])
temperatures = ["HOT", "LUKEWARM", "ICED"]
def _impl(ctx):
raw_temperature = ctx.build_setting_value
if raw_temperature not in temperatures:
fail(str(ctx.label) + " build setting allowed to take values {"
+ ", ".join(temperatures) + "} but was set to unallowed value "
+ raw_temperature)
return TemperatureProvider(type = raw_temperature)
temperature = rule(
implementation = _impl,
build_setting = config.string(flag = True)
)
Xác định cờ chuỗi nhiều tập hợp
Chế độ cài đặt chuỗi có thêm tham số allow_multiple
cho phép đặt cờ nhiều lần trên dòng lệnh hoặc trong bazelrc. Giá trị mặc định của chúng vẫn được đặt bằng một thuộc tính có kiểu chuỗi:
# example/buildsettings/build_settings.bzl
allow_multiple_flag = rule(
implementation = _impl,
build_setting = config.string(flag = True, allow_multiple = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "allow_multiple_flag")
allow_multiple_flag(
name = "roasts",
build_setting_default = "medium"
)
Mỗi chế độ cài đặt của cờ được coi là một giá trị duy nhất:
$ bazel build //my/target --//example:roasts=blonde \
--//example:roasts=medium,dark
Đoạn mã trên được phân tích cú pháp thành {"//example:roasts": ["blonde", "medium,dark"]}
và ctx.build_setting_value
trả về danh sách ["blonde", "medium,dark"]
.
Tạo thực thể cho chế độ cài đặt bản dựng
Các quy tắc được xác định bằng tham số build_setting
có một thuộc tính build_setting_default
bắt buộc ngầm ẩn. Thuộc tính này có cùng loại với tham số build_setting
đã khai báo.
# example/buildsettings/build_settings.bzl
FlavorProvider = provider(fields = ['type'])
def _impl(ctx):
return FlavorProvider(type = ctx.build_setting_value)
flavor = rule(
implementation = _impl,
build_setting = config.string(flag = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
name = "favorite_flavor",
build_setting_default = "APPLE"
)
Chế độ cài đặt được xác định trước
Thư viện Skylib bao gồm một tập hợp các chế độ cài đặt được xác định trước mà bạn có thể tạo thực thể mà không cần phải viết Starlark tuỳ chỉnh.
Ví dụ: để xác định một chế độ cài đặt chấp nhận một tập hợp giá trị chuỗi có giới hạn:
# example/BUILD
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
string_flag(
name = "myflag",
values = ["a", "b", "c"],
build_setting_default = "a",
)
Để xem danh sách đầy đủ, hãy xem Các quy tắc cài đặt bản dựng phổ biến.
Sử dụng chế độ cài đặt bản dựng
Tuỳ thuộc vào chế độ cài đặt bản dựng
Nếu muốn đọc một phần thông tin cấu hình, mục tiêu có thể trực tiếp phụ thuộc vào chế độ cài đặt bản dựng thông qua một phần phụ thuộc thuộc tính thông thường.
# example/rules.bzl
load("//example/buildsettings:build_settings.bzl", "FlavorProvider")
def _rule_impl(ctx):
if ctx.attr.flavor[FlavorProvider].type == "ORANGE":
...
drink_rule = rule(
implementation = _rule_impl,
attrs = {
"flavor": attr.label()
}
)
# example/BUILD
load("//example:rules.bzl", "drink_rule")
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
name = "favorite_flavor",
build_setting_default = "APPLE"
)
drink_rule(
name = "my_drink",
flavor = ":favorite_flavor",
)
Các ngôn ngữ có thể muốn tạo một bộ chế độ cài đặt bản dựng chuẩn mà tất cả các quy tắc cho ngôn ngữ đó đều phụ thuộc vào. Mặc dù khái niệm gốc về fragments
không còn tồn tại dưới dạng một đối tượng được mã hoá cứng trong thế giới cấu hình Starlark, nhưng một cách để dịch khái niệm này là sử dụng các tập hợp thuộc tính ngầm định chung. Ví dụ:
# kotlin/rules.bzl
_KOTLIN_CONFIG = {
"_compiler": attr.label(default = "//kotlin/config:compiler-flag"),
"_mode": attr.label(default = "//kotlin/config:mode-flag"),
...
}
...
kotlin_library = rule(
implementation = _rule_impl,
attrs = dicts.add({
"library-attr": attr.string()
}, _KOTLIN_CONFIG)
)
kotlin_binary = rule(
implementation = _binary_impl,
attrs = dicts.add({
"binary-attr": attr.label()
}, _KOTLIN_CONFIG)
Sử dụng chế độ cài đặt bản dựng trên dòng lệnh
Tương tự như hầu hết các cờ gốc, bạn có thể dùng dòng lệnh để đặt các chế độ cài đặt bản dựng được đánh dấu là cờ. Tên của chế độ cài đặt bản dựng là đường dẫn mục tiêu đầy đủ bằng cú pháp name=value
:
$ bazel build //my/target --//example:string_flag=some-value # allowed
$ bazel build //my/target --//example:string_flag some-value # not allowed
Hỗ trợ cú pháp boolean đặc biệt:
$ bazel build //my/target --//example:boolean_flag
$ bazel build //my/target --no//example:boolean_flag
Sử dụng các bí danh chế độ cài đặt bản dựng
Bạn có thể đặt một bí danh cho đường dẫn mục tiêu của chế độ cài đặt bản dựng để dễ đọc hơn trên dòng lệnh. Các bí danh hoạt động tương tự như các cờ gốc và cũng sử dụng cú pháp tuỳ chọn dấu gạch ngang kép.
Đặt một bí danh bằng cách thêm --flag_alias=ALIAS_NAME=TARGET_PATH
vào .bazelrc
. Ví dụ: để đặt một biệt hiệu cho coffee
:
# .bazelrc
build --flag_alias=coffee=//experimental/user/starlark_configurations/basic_build_setting:coffee-temp
Cách hay nhất: Việc đặt một bí danh nhiều lần sẽ khiến bí danh gần đây nhất được ưu tiên. Sử dụng tên bí danh riêng biệt để tránh kết quả phân tích cú pháp không mong muốn.
Để sử dụng bí danh, hãy nhập bí danh đó thay cho đường dẫn mục tiêu của chế độ cài đặt bản dựng.
Với ví dụ trên về coffee
được đặt trong .bazelrc
của người dùng:
$ bazel build //my/target --coffee=ICED
thay vì
$ bazel build //my/target --//experimental/user/starlark_configurations/basic_build_setting:coffee-temp=ICED
Phương pháp hay nhất: Mặc dù có thể đặt bí danh trên dòng lệnh, nhưng việc để chúng trong .bazelrc
sẽ giúp dòng lệnh gọn gàng hơn.
Chế độ cài đặt bản dựng được gắn nhãn
Không giống như các chế độ cài đặt bản dựng khác, bạn không thể xác định chế độ cài đặt được nhập bằng nhãn bằng cách sử dụng tham số quy tắc build_setting
. Thay vào đó, bazel có hai quy tắc tích hợp: label_flag
và label_setting
. Các quy tắc này chuyển tiếp nhà cung cấp của mục tiêu thực tế mà chế độ cài đặt bản dựng được đặt. label_flag
và label_setting
có thể được các hiệu ứng chuyển cảnh đọc/ghi và người dùng có thể đặt label_flag
như các quy tắc build_setting
khác. Điểm khác biệt duy nhất giữa hai loại này là chúng không thể được xác định tuỳ chỉnh.
Chế độ cài đặt được nhập bằng nhãn cuối cùng sẽ thay thế chức năng của các giá trị mặc định liên kết muộn. Các thuộc tính mặc định liên kết muộn là các thuộc tính được nhập bằng nhãn mà giá trị cuối cùng có thể bị ảnh hưởng bởi cấu hình. Trong Starlark, thao tác này sẽ thay thế API configuration_field
.
# example/rules.bzl
MyProvider = provider(fields = ["my_field"])
def _dep_impl(ctx):
return MyProvider(my_field = "yeehaw")
dep_rule = rule(
implementation = _dep_impl
)
def _parent_impl(ctx):
if ctx.attr.my_field_provider[MyProvider].my_field == "cowabunga":
...
parent_rule = rule(
implementation = _parent_impl,
attrs = { "my_field_provider": attr.label() }
)
# example/BUILD
load("//example:rules.bzl", "dep_rule", "parent_rule")
dep_rule(name = "dep")
parent_rule(name = "parent", my_field_provider = ":my_field_provider")
label_flag(
name = "my_field_provider",
build_setting_default = ":dep"
)
Build settings and select()
Người dùng có thể định cấu hình các thuộc tính trong phần cài đặt bản dựng bằng cách sử dụng select()
. Bạn có thể truyền các mục tiêu cài đặt bản dựng đến thuộc tính flag_values
của config_setting
. Giá trị cần khớp với cấu hình được truyền dưới dạng String
, sau đó được phân tích cú pháp thành loại của chế độ cài đặt bản dựng để khớp.
config_setting(
name = "my_config",
flag_values = {
"//example:favorite_flavor": "MANGO"
}
)
Hiệu ứng chuyển cảnh do người dùng xác định
Một quá trình chuyển đổi cấu hình sẽ liên kết quá trình chuyển đổi từ một mục tiêu được định cấu hình sang một mục tiêu khác trong biểu đồ bản dựng.
Các quy tắc đặt những thuộc tính này phải có một thuộc tính đặc biệt:
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist"
)
Bằng cách thêm các hiệu ứng chuyển đổi, bạn có thể dễ dàng tăng kích thước của biểu đồ bản dựng. Thao tác này sẽ đặt một danh sách cho phép trên các gói mà bạn có thể tạo mục tiêu của quy tắc này. Giá trị mặc định trong khối mã ở trên cho phép mọi thứ. Nhưng nếu muốn hạn chế những người sử dụng quy tắc của bạn, bạn có thể đặt thuộc tính đó để trỏ đến danh sách cho phép tuỳ chỉnh của riêng bạn. Hãy liên hệ với bazel-discuss@googlegroups.com nếu bạn muốn được tư vấn hoặc trợ giúp để hiểu cách các quá trình chuyển đổi có thể ảnh hưởng đến hiệu suất bản dựng của bạn.
Xác định
Các hiệu ứng chuyển đổi xác định những thay đổi về cấu hình giữa các quy tắc. Ví dụ: một yêu cầu như "biên dịch phần phụ thuộc của tôi cho một CPU khác với CPU mẹ" sẽ được xử lý bằng một quá trình chuyển đổi.
Về mặt hình thức, quá trình chuyển đổi là một hàm từ cấu hình đầu vào đến một hoặc nhiều cấu hình đầu ra. Hầu hết các quá trình chuyển đổi đều là 1:1, chẳng hạn như "ghi đè cấu hình đầu vào bằng --cpu=ppc
". Quá trình chuyển đổi 1:2+ cũng có thể tồn tại nhưng đi kèm với các hạn chế đặc biệt.
Trong Starlark, các quá trình chuyển đổi được xác định giống như các quy tắc, với một transition()
xác định hàm và một hàm triển khai.
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {"//example:favorite_flavor" : "MINT"}
hot_chocolate_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//example:favorite_flavor"]
)
Hàm transition()
nhận một hàm triển khai, một tập hợp các chế độ cài đặt bản dựng để đọc(inputs
) và một tập hợp các chế độ cài đặt bản dựng để ghi (outputs
). Hàm triển khai có hai tham số, settings
và attr
. settings
là một từ điển {String
:Object
} của tất cả các chế độ cài đặt được khai báo trong tham số inputs
thành transition()
.
attr
là một từ điển gồm các thuộc tính và giá trị của quy tắc mà quá trình chuyển đổi được đính kèm. Khi được đính kèm dưới dạng một chuyển đổi cạnh đi, các giá trị của tất cả các thuộc tính này đều được định cấu hình sau khi phân giải select(). Khi được đính kèm dưới dạng một chuyển đổi cạnh đến, attr
sẽ không bao gồm bất kỳ thuộc tính nào sử dụng bộ chọn để phân giải giá trị của chúng. Nếu một quá trình chuyển đổi cạnh đến trên --foo
đọc thuộc tính bar
rồi cũng chọn trên --foo
để đặt thuộc tính bar
, thì có khả năng quá trình chuyển đổi cạnh đến sẽ đọc sai giá trị của bar
trong quá trình chuyển đổi.
Hàm triển khai phải trả về một từ điển (hoặc danh sách từ điển, trong trường hợp chuyển đổi có nhiều cấu hình đầu ra) gồm các giá trị chế độ cài đặt bản dựng mới để áp dụng. (Các) khoá từ điển được trả về phải chứa chính xác bộ chế độ cài đặt bản dựng được truyền đến tham số outputs
của hàm chuyển đổi. Điều này đúng ngay cả khi chế độ cài đặt bản dựng không thực sự thay đổi trong quá trình chuyển đổi – giá trị ban đầu của chế độ cài đặt đó phải được truyền một cách rõ ràng thông qua từ điển được trả về.
Xác định các chuyển đổi 1:2+
Chuyển đổi cạnh đi có thể ánh xạ một cấu hình đầu vào duy nhất thành hai hoặc nhiều cấu hình đầu ra. Điều này hữu ích cho việc xác định các quy tắc kết hợp mã nhiều kiến trúc.
Các hiệu ứng chuyển đổi 1:2+ được xác định bằng cách trả về một danh sách các từ điển trong hàm triển khai hiệu ứng chuyển đổi.
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return [
{"//example:favorite_flavor" : "LATTE"},
{"//example:favorite_flavor" : "MOCHA"},
]
coffee_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//example:favorite_flavor"]
)
Chúng cũng có thể đặt các khoá tuỳ chỉnh mà hàm triển khai quy tắc có thể dùng để đọc các phần phụ thuộc riêng lẻ:
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {
"Apple deps": {"//command_line_option:cpu": "ppc"},
"Linux deps": {"//command_line_option:cpu": "x86"},
}
multi_arch_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//command_line_option:cpu"]
)
Đính kèm hiệu ứng chuyển cảnh
Bạn có thể đính kèm hiệu ứng chuyển cảnh ở hai vị trí: cạnh đầu vào và cạnh đầu ra. Điều này có nghĩa là các quy tắc có thể chuyển đổi cấu hình của riêng chúng (chuyển đổi cạnh đến) và chuyển đổi cấu hình của các phần phụ thuộc (chuyển đổi cạnh đi).
LƯU Ý: Hiện không có cách nào để đính kèm các quá trình chuyển đổi Starlark vào các quy tắc gốc. Nếu bạn cần làm việc này, hãy liên hệ với bazel-discuss@googlegroups.com để được trợ giúp tìm ra các giải pháp thay thế.
Hiệu ứng chuyển đổi cạnh đi vào
Các hiệu ứng chuyển đổi cạnh đến được kích hoạt bằng cách đính kèm một đối tượng transition
(do transition()
tạo) vào tham số cfg
của rule()
:
# example/rules.bzl
load("example/transitions:transitions.bzl", "hot_chocolate_transition")
drink_rule = rule(
implementation = _impl,
cfg = hot_chocolate_transition,
...
Các hiệu ứng chuyển cảnh đến phải là hiệu ứng chuyển cảnh 1:1.
Chuyển đổi cạnh đi
Các hiệu ứng chuyển đổi cạnh đi được kích hoạt bằng cách đính kèm một đối tượng transition
(do transition()
tạo) vào tham số cfg
của một thuộc tính:
# example/rules.bzl
load("example/transitions:transitions.bzl", "coffee_transition")
drink_rule = rule(
implementation = _impl,
attrs = { "dep": attr.label(cfg = coffee_transition)}
...
Hiệu ứng chuyển cảnh ở cạnh ngoài có thể là 1:1 hoặc 1:2+.
Hãy xem phần Truy cập vào các thuộc tính bằng hiệu ứng chuyển đổi để biết cách đọc các khoá này.
Chuyển đổi trên các lựa chọn gốc
Các quá trình chuyển đổi Starlark cũng có thể khai báo các thao tác đọc và ghi trên các lựa chọn cấu hình bản dựng gốc thông qua một tiền tố đặc biệt cho tên lựa chọn.
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {"//command_line_option:cpu": "k8"}
cpu_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//command_line_option:cpu"]
Các lựa chọn gốc không được hỗ trợ
Bazel không hỗ trợ chuyển đổi trên --define
bằng "//command_line_option:define"
. Thay vào đó, hãy sử dụng chế độ cài đặt bản dựng tuỳ chỉnh. Nhìn chung, bạn không nên sử dụng --define
mà nên dùng chế độ cài đặt bản dựng.
Bazel không hỗ trợ chuyển đổi trên --config
. Lý do là vì --config
là một cờ "mở rộng" sẽ mở rộng sang các cờ khác.
Điều quan trọng là --config
có thể bao gồm các cờ không ảnh hưởng đến cấu hình bản dựng, chẳng hạn như --spawn_strategy
. Theo thiết kế, Bazel không thể liên kết các cờ như vậy với các mục tiêu riêng lẻ. Điều này có nghĩa là không có cách nhất quán nào để áp dụng chúng trong các hiệu ứng chuyển đổi.
Để khắc phục, bạn có thể liệt kê rõ ràng các cờ là một phần của cấu hình trong quá trình chuyển đổi. Việc này đòi hỏi bạn phải duy trì việc mở rộng --config
ở hai nơi, đây là một lỗi giao diện người dùng đã biết.
Chuyển đổi trên nhiều chế độ cài đặt bản dựng
Khi đặt các chế độ cài đặt bản dựng cho phép nhiều giá trị, bạn phải đặt giá trị của chế độ cài đặt bằng một danh sách.
# example/buildsettings/build_settings.bzl
string_flag = rule(
implementation = _impl,
build_setting = config.string(flag = True, allow_multiple = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "string_flag")
string_flag(name = "roasts", build_setting_default = "medium")
# example/transitions/rules.bzl
def _transition_impl(settings, attr):
# Using a value of just "dark" here will throw an error
return {"//example:roasts" : ["dark"]},
coffee_transition = transition(
implementation = _transition_impl,
inputs = [],
outputs = ["//example:roasts"]
)
Chuyển đổi không hoạt động
Nếu một hiệu ứng chuyển đổi trả về {}
, []
hoặc None
, thì đây là cách viết tắt để giữ tất cả các chế độ cài đặt ở giá trị ban đầu. Điều này có thể thuận tiện hơn so với việc đặt rõ ràng từng đầu ra cho chính nó.
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (attr)
if settings["//example:already_chosen"] is True:
return {}
return {
"//example:favorite_flavor": "dark chocolate",
"//example:include_marshmallows": "yes",
"//example:desired_temperature": "38C",
}
hot_chocolate_transition = transition(
implementation = _impl,
inputs = ["//example:already_chosen"],
outputs = [
"//example:favorite_flavor",
"//example:include_marshmallows",
"//example:desired_temperature",
]
)
Truy cập vào các thuộc tính bằng hiệu ứng chuyển đổi
Khi đính kèm một hiệu ứng chuyển đổi vào một cạnh đi (bất kể hiệu ứng chuyển đổi là hiệu ứng chuyển đổi 1:1 hay 1:2+), ctx.attr
buộc phải là một danh sách nếu chưa phải. Thứ tự của các phần tử trong danh sách này không được chỉ định.
# example/transitions/rules.bzl
def _transition_impl(settings, attr):
return {"//example:favorite_flavor" : "LATTE"},
coffee_transition = transition(
implementation = _transition_impl,
inputs = [],
outputs = ["//example:favorite_flavor"]
)
def _rule_impl(ctx):
# Note: List access even though "dep" is not declared as list
transitioned_dep = ctx.attr.dep[0]
# Note: Access doesn't change, other_deps was already a list
for other_dep in ctx.attr.other_deps:
# ...
coffee_rule = rule(
implementation = _rule_impl,
attrs = {
"dep": attr.label(cfg = coffee_transition)
"other_deps": attr.label_list(cfg = coffee_transition)
})
Nếu quá trình chuyển đổi là 1:2+
và đặt các khoá tuỳ chỉnh, thì bạn có thể dùng ctx.split_attr
để đọc các phần phụ thuộc riêng lẻ cho từng khoá:
# example/transitions/rules.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {
"Apple deps": {"//command_line_option:cpu": "ppc"},
"Linux deps": {"//command_line_option:cpu": "x86"},
}
multi_arch_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//command_line_option:cpu"]
)
def _rule_impl(ctx):
apple_dep = ctx.split_attr.dep["Apple deps"]
linux_dep = ctx.split_attr.dep["Linux deps"]
# ctx.attr has a list of all deps for all keys. Order is not guaranteed.
all_deps = ctx.attr.dep
multi_arch_rule = rule(
implementation = _rule_impl,
attrs = {
"dep": attr.label(cfg = multi_arch_transition)
})
Xem ví dụ đầy đủ tại đây.
Tích hợp với các nền tảng và chuỗi công cụ
Nhiều cờ gốc hiện nay, chẳng hạn như --cpu
và --crosstool_top
, có liên quan đến việc phân giải chuỗi công cụ. Trong tương lai, các quá trình chuyển đổi rõ ràng trên những loại cờ này có thể sẽ được thay thế bằng quá trình chuyển đổi trên nền tảng mục tiêu.
Những điểm cần cân nhắc về bộ nhớ và hiệu suất
Việc thêm hiệu ứng chuyển cảnh (và do đó là cấu hình mới) vào bản dựng sẽ phải trả giá: đồ thị bản dựng lớn hơn, đồ thị bản dựng khó hiểu hơn và bản dựng chậm hơn. Bạn nên cân nhắc những chi phí này khi xem xét việc sử dụng các hiệu ứng chuyển đổi trong quy tắc tạo bản dựng. Dưới đây là ví dụ về cách một quá trình chuyển đổi có thể tạo ra sự tăng trưởng theo cấp số nhân cho biểu đồ bản dựng của bạn.
Các bản dựng hoạt động không đúng cách: một nghiên cứu điển hình
Hình 1. Biểu đồ khả năng mở rộng cho thấy mục tiêu cấp cao nhất và các phần phụ thuộc của mục tiêu đó.
Biểu đồ này cho thấy một mục tiêu cấp cao nhất, //pkg:app
, phụ thuộc vào 2 mục tiêu, //pkg:1_0
và //pkg:1_1
. Cả hai mục tiêu này đều phụ thuộc vào hai mục tiêu là //pkg:2_0
và //pkg:2_1
. Cả hai mục tiêu này đều phụ thuộc vào hai mục tiêu là //pkg:3_0
và //pkg:3_1
.
Quá trình này tiếp tục cho đến //pkg:n_0
và //pkg:n_1
, cả hai đều phụ thuộc vào một mục tiêu duy nhất là //pkg:dep
.
Việc tạo //pkg:app
yêu cầu \(2n+2\) các mục tiêu:
//pkg:app
//pkg:dep
//pkg:i_0
và//pkg:i_1
cho \(i\) trong \([1..n]\)
Hãy tưởng tượng bạn triển khai một cờ --//foo:owner=<STRING>
và //pkg:i_b
áp dụng
depConfig = myConfig + depConfig.owner="$(myConfig.owner)$(b)"
Nói cách khác, //pkg:i_b
sẽ nối b
vào giá trị cũ của --owner
cho tất cả các deps của nó.
Thao tác này sẽ tạo ra các mục tiêu được định cấu hình sau đây:
//pkg:app //foo:owner=""
//pkg:1_0 //foo:owner=""
//pkg:1_1 //foo:owner=""
//pkg:2_0 (via //pkg:1_0) //foo:owner="0"
//pkg:2_0 (via //pkg:1_1) //foo:owner="1"
//pkg:2_1 (via //pkg:1_0) //foo:owner="0"
//pkg:2_1 (via //pkg:1_1) //foo:owner="1"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_0) //foo:owner="00"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_1) //foo:owner="01"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_0) //foo:owner="10"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_1) //foo:owner="11"
...
//pkg:dep
tạo ra \(2^n\) các mục tiêu đã định cấu hình: config.owner=
"\(b_0b_1...b_n\)" cho tất cả \(b_i\) trong \(\{0,1\}\).
Điều này khiến biểu đồ bản dựng lớn hơn biểu đồ mục tiêu theo cấp số nhân, với các hậu quả tương ứng về bộ nhớ và hiệu suất.
TODO: Thêm các chiến lược đo lường và giảm thiểu những vấn đề này.
Tài liệu đọc thêm
Để biết thêm thông tin chi tiết về cách sửa đổi cấu hình bản dựng, hãy xem:
- Cấu hình bản dựng Starlark
- Lộ trình có thể định cấu hình của Bazel
- Tập đầy đủ các ví dụ từ đầu đến cuối