Phiên bản

Báo cáo sự cố Xem nguồn

Trang này trình bày các lợi ích và cách cơ bản sử dụng cấu hình Starlark, API của Bazel để tuỳ chỉnh cách xây dựng dự án. Hướng dẫn này bao gồm cách xác định các chế độ cài đặt của bản dựng và đưa ra các ví dụ.

Điều này giúp bạn có thể:

  • xác định cờ tuỳ chỉnh cho dự án, không cần dùng --define
  • ghi các lượt chuyển đổi để định cấu hình phần phụ thuộc ở các cấu hình khác với cấu hình gốc (chẳng hạn như --compilation_mode=opt hoặc --cpu=arm)
  • tích hợp các quy tắc mặc định tốt hơn vào các quy tắc (chẳng hạn như tự động tạo //my:android_app bằng một SDK cụ thể)

và hơn thế nữa, tất cả đều hoàn toàn từ tệp .bzl (không cầ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 duy nhất. Hãy xem cấu hình như một sơ đồ khoá/giá trị. Việc đặt --cpu=ppc--copt="-DFoo" sẽ tạo ra một cấu hình giống như {cpu: ppc, copt: "-DFoo"}. Mỗi mục nhập là một chế độ cài đặt bản dựng.

Cờ truyền thống như cpucopt là các chế độ cài đặt gốc — các khoá của chúng được xác định và giá trị được đặt bên trong mã Java bazel gốc. Người dùng Bazel chỉ có thể đọc và ghi các API đó thông qua dòng lệnh và các API khác được duy trì nguyên gốc. Việc thay đổi cờ gốc và các API hiển thị những cờ này đòi hỏi phải có 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ý thay đổi). Bạn cũng có thể đặt các biến 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 qua các lượt chuyển đổi do người dùng xác định.

Xác định chế độ cài đặt bản dựng

Ví dụ từ đầu đến cuối

Thông 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 thuộc tính build_setting của hàm Starlark rule().

# example/buildsettings/build_settings.bzl
string_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)

Thuộc tính build_setting nhận một hàm chỉ định loại chế độ cài đặt bản dựng. Loại này chỉ giới hạn ở một tập hợp các kiểu Starlark cơ bản như boolstring. Xem tài liệu về mô-đun config để biết thông tin chi tiết. Bạn có thể nhập nội dung phức tạp hơn trong hàm triển khai của quy tắc. Tìm hiểu thêm về điều này ở bên dưới.

Các hàm của mô-đun config sẽ lấy một tham số boolean không bắt buộc là flag. Tham số này được đặt thành false theo mặc định. Nếu flag được đặt thành true, người dùng cũng như nội bộ có thể đặt chế độ cài đặt bản dựng trên dòng lệnh thông qua các giá trị mặc định và hoạt động chuyển đổi. Người dùng không thể thiết lập được tất cả các chế độ cài đặt. Ví dụ: nếu là người viết quy tắc có một số chế độ gỡ lỗi cần bật bên trong quy tắc kiểm thử, bạn không nên để người dùng bật một cách bừa bãi tính năng đó bên trong các quy tắc không kiểm thử khác.

Sử dụng ctx.build_setting_value

Giống như tất cả các quy tắc, quy tắc cài đặt bản dựng có hàm triển khai. Bạn có thể truy cập vào giá trị loại Starlark cơ bản 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 một số thao tác trên đó, chẳng hạn như kiểm tra loại hoặc tạo cấu trúc phức tạp hơn. Dưới đây là cách bạn sẽ triển khai chế độ cài đặt bản dựng 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 một tham số allow_multiple cho phép đặt cờ nhiều lần trên dòng lệnh hoặc trong bazelrcs. Giá trị mặc định của trường vẫn được đặt bằng thuộc tính dạng 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

Nội dung trên được phân tích cú pháp thành {"//example:roasts": ["blonde", "medium,dark"]}ctx.build_setting_value trả về danh sách ["blonde", "medium,dark"].

Tạo thực thể của chế độ cài đặt bản dựng

Các quy tắc được xác định bằng tham số build_setting có thuộc tính build_setting_default bắt buộc ngầm ẩn. Thuộc tính này lấy cùng một loại do 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"
)

Cài đặt xác định trước

Ví dụ từ đầu đến cuối

Thư viện Skylib bao gồm một tập hợp các chế độ cài đặt định sẵn 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 chế độ cài đặt chấp nhận một tập hợp giá trị chuỗi giới hạn, hãy làm như sau:

# 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 phần 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 mục tiêu muốn đọc một phần thông tin cấu hình, thì 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ữ nên tạo một bộ chế độ cài đặt bản dựng chuẩn hoá mà tất cả các quy tắc cho ngôn ngữ đó sẽ phụ thuộc vào. Mặc dù khái niệm gốc của fragments không còn tồn tại dưới dạng đối tượng được mã hoá cứng trong thế giới cấu hình Starlark, nhưng bạn có thể dịch khái niệm này bằng cách sử dụng các tập hợp các thuộc tính ngầm ẩn phổ biến. 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ờ gốc, bạn có thể sử dụng dòng lệnh để thiết lập 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 đủ sử dụ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

Cú pháp boolean đặc biệt được hỗ trợ:

$ bazel build //my/target --//example:boolean_flag
$ bazel build //my/target --no//example:boolean_flag

Sử dụng bí danh cài đặt bản dựng

Bạn có thể đặt một đại diện 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. Bí danh hoạt động tương tự như cờ gốc và cũng sử dụng cú pháp tuỳ chọn dấu gạch ngang đôi.

Đặt bí danh bằng cách thêm --flag_alias=ALIAS_NAME=TARGET_PATH vào .bazelrc của bạn . Ví dụ: để đặt bí danh thành coffee:

# .bazelrc
build --flag_alias=coffee=//experimental/user/starlark_configurations/basic_build_setting:coffee-temp

Phương pháp hay nhất: Việc đặt một bí danh nhiều lần sẽ dẫn đến việc bí danh gần đây nhất sẽ được ưu tiên. Hãy sử dụng tên đại diện riêng biệt để tránh việc phân tích cú pháp những kết quả ngoài ý 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 các bí danh trên dòng lệnh, nhưng việc để các bí danh đó trong một .bazelrc sẽ làm giảm sự lộn xộn của dòng lệnh.

Cài đặt bản dựng được nhập nhãn

Ví dụ từ đầu đến cuối

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 các chế độ cài đặt theo loại nhãn bằng tham số quy tắc build_setting. Thay vào đó, bazel có 2 quy tắc tích hợp sẵn: label_flaglabel_setting. Các quy tắc này chuyển tiếp các nhà cung cấp mục tiêu thực tế mà chế độ cài đặt bản dựng đã đặt. Quá trình chuyển đổi có thể đọc/ghi label_flaglabel_setting, còn 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 của chúng là không thể xác định tuỳ chỉnh.

Cuối cùng, các chế độ cài đặt nhập nhãn sẽ thay thế chức năng của các giá trị mặc định bị ràng buộc muộn. Thuộc tính mặc định giới hạn muộn là các thuộc tính có loại Nhãn mà giá trị cuối cùng có thể bị ảnh hưởng bởi cấu hình. Trong Starlark, API 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"
)

Tạo chế độ cài đặt và chọn()

Ví dụ từ đầu đến cuối

Người dùng có thể định cấu hình các thuộc tính trên 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 so 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 chế độ cài đặt bản dựng để so khớp.

config_setting(
    name = "my_config",
    flag_values = {
        "//example:favorite_flavor": "MANGO"
    }
)

Hiệu ứng chuyển đổi do người dùng xác định

Một lần chuyển đổi cấu hình sẽ ánh xạ 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.

Xác định

Quá trình 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 cho một CPU khác với CPU mẹ" sẽ được xử lý trong quá trình chuyển đổi.

Về cơ bản, chuyển đổi là một hàm từ cấu hình đầu vào sang một hoặc nhiều cấu hình đầu ra. Hầu hết các lượt chuyển đổi đều là 1:1, chẳng hạn như "ghi đè cấu hình đầu vào bằng --cpu=ppc". Hiệu ứng chuyển đổi 1:2 trở lên 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, hiệu ứng chuyển đổi được xác định giống như các quy tắc, với một hàm transition() xác định 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() có 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ố settingsattr. settings là từ điển {String:Object} của tất cả các chế độ cài đặt được khai báo trong tham số inputs cho transition().

attr là từ điển về các thuộc tính và giá trị của quy tắc đính kèm lượt chuyển đổi. Khi được đính kèm dưới dạng chuyển đổi cạnh đi, giá trị của các thuộc tính này đều là độ phân giải hậu chọn() được định cấu hình. Khi được đính kèm dưới dạng chuyển đổi cạnh sắp đến, attr không bao gồm bất kỳ thuộc tính nào dùng bộ chọn để phân giải giá trị của các thuộc tính đó. Nếu một chuyển đổi cạnh đến trên --foo đọc thuộc tính bar, sau đó cũng chọn trên --foo để đặt thuộc tính bar, thì có khả năng hiệu ứng chuyển đổi cạnh đến sẽ đọc giá trị không chính xác của bar trong quá trình chuyển đổi.

Hàm triển khai phải trả về 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) của các giá trị cài đặt bản dựng mới để áp dụng. (Các) bộ khoá từ điển được trả về phải chứa chính xác tập hợp các 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 vẫn xảy ra ngay cả khi một 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ế độ đó phải được chuyển một cách rõ ràng trong từ điển được trả về.

Xác định quá trình chuyển đổi 1:2+

Ví dụ từ đầu đến cuối

Chuyển đổi cạnh đi có thể ánh xạ một cấu hình đầu vào với hai hoặc nhiều cấu hình đầu ra. Điều này rất hữu ích khi xác định các quy tắc gói mã đa kiến trúc.

Hiệu ứng chuyển đổi 1:2 trở lên được xác định bằng cách trả về một danh sách từ điển trong hàm triển khai 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ể sử 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

Ví dụ từ đầu đến cuối

Bạn có thể đính kèm hiệu ứng chuyển đổi ở hai vị trí: cạnh đến và cạnh đi. Đ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 tới) 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 chuyển đổi Starlark vào 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-Talk@googlegroups.com để được trợ giúp tìm giải pháp.

Chuyển đổi cạnh đến

Hiệu ứng chuyển đổi cạnh đến được kích hoạt bằng cách đính kèm đố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,
    ...

Chuyển đổi cạnh đến phải là chuyển đổi 1:1.

Chuyển đổi cạnh đi

Kích hoạt hiệu ứng chuyển đổi cạnh đi được kích hoạt bằng cách đính kèm đối tượng transition (do transition() tạo) vào tham số cfg của 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 đổi cạnh đi có thể là 1:1 hoặc 1:2+.

Xem bài viết Truy cập vào các thuộc tính có hiệu ứng chuyển đổi để biết cách đọc các khoá này.

Chuyển đổi trên tùy chọn gốc

Ví dụ từ đầu đến cuối

Chuyển đổi Starlark cũng có thể khai báo lượt đọc và ghi trên các tuỳ 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 tuỳ 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"]

Tùy 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ới mà thay vào đó là chế độ cài đặt bản dựng.

Bazel không hỗ trợ chuyển đổi trên --config. Lý do là --config là một cờ "mở rộng" mở rộng sang các cờ khác.

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 từng mục tiêu riêng lẻ. Điều này có nghĩa là không có cách nhất quán để áp dụng các biến này trong quá trình chuyển đổi.

Để khắc phục vấn đề này, bạn có thể phân loại rõ ràng các cờ một phần của cấu hình trong quá trình chuyển đổi thành từng mục. Điều này yêu cầu phải duy trì sự mở rộng của --config ở hai vị trí (đây là lỗi giao diện người dùng đã biết).

Quá trình chuyển đổi đang bật cho phép nhiều chế độ cài đặt bản dựng

Khi thiết lập 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 quá trình chuyển đổi trả về {}, [] hoặc None, thì đây là cách viết tắt để giữ tất cả chế độ cài đặt ở giá trị ban đầu. Cách 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 có hiệu ứng chuyển đổi

Ví dụ từ đầu đến cuối

Khi đính kèm lượt chuyển đổi vào một cạnh đi (bất kể lượt chuyển đổi là lượt chuyển đổi 1:1 hay 1:2 trở lên), ctx.attr bắt buộc phải ở dạng danh sách nếu chưa thực hiện. Chưa xác định thứ tự của các phần tử trong danh sách này.

# 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 chuyển đổi là 1:2+ và đặt các khoá tuỳ chỉnh, thì 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--crosstool_top, đều liên quan đến độ phân giải của chuỗi công cụ. Trong tương lai, việc chuyển đổi rõ ràng trên các loại cờ này có thể sẽ được thay thế bằng cách chuyển đổi trên nền tảng mục tiêu.

Những điều cần cân nhắc về bộ nhớ và hiệu suất

Việc thêm các lượt chuyển đổi và theo đó là cấu hình mới vào bản dựng sẽ gây ra hao tổn: biểu đồ bản dựng lớn hơn, biểu đồ bản dựng ít dễ hiểu hơn và các bản dựng chậm hơn. Bạn nên cân nhắc đến những chi phí này khi cân nhắc sử dụng 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 quá trình chuyển đổi có thể tạo ra sự tăng trưởng theo cấp số nhân của biểu đồ bản dựng.

Bản dựng có hành vi xấu: một nghiên cứu điển hình

Biểu đồ về khả năng mở rộng

Hình 1. Biểu đồ về khả năng mở rộng cho thấy một 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 là //pkg:app, phụ thuộc vào hai mục tiêu là //pkg:1_0//pkg:1_1. Cả hai mục tiêu này đều phụ thuộc vào 2 mục tiêu là //pkg:2_0//pkg:2_1. Cả hai mục tiêu này đều phụ thuộc vào 2 mục tiêu là //pkg:3_0//pkg:3_1. Quá trình này sẽ tiếp tục cho đến //pkg:n_0//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\) mục tiêu:

  • //pkg:app
  • //pkg:dep
  • //pkg:i_0//pkg:i_1 trong \(i\) \([1..n]\)

Giả sử bạn implement một cờ --//foo:owner=<STRING>//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 phần phụ thuộc.

Thao tác này sẽ tạo ra các mục tiêu được định cấu hình sau:

//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 được đị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 làm cho biểu đồ bản dựng lớn hơn biểu đồ mục tiêu theo cấp số nhân, kèm theo các hậu quả tương ứng về hiệu suất và bộ nhớ.

VIỆC CẦN LÀM: Thêm 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 về cách sửa đổi cấu hình bản dựng, hãy xem: