Phiên bản

Trang này trình bày về 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 xây dựng dự án. Trang này 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, loại bỏ nhu cầu sử dụng --define
  • viết 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 phụ thuộc mẹ (chẳng hạn như --compilation_mode=opt hoặc --cpu=arm)
  • tạo các giá trị mặc định tốt hơn vào quy tắc (chẳng hạn như tự động xây dựng //my:android_app bằng một SDK được chỉ định)

và nhiều tính năng 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--copt="-DFoo" sẽ tạo ra một cấu hình có dạng như {cpu: ppc, copt: "-DFoo"}. Mỗi mục nhập là một chế độ cài đặt bản dựng.

Các cờ truyền thống như cpucopt là 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 cờ 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ác 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 chế độ cài đặt 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 quá trình 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

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

# 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 bị giới hạn ở một tập hợp các loại Starlark cơ bản như boolstring. Hãy xem tài liệu về mô-đun config documentation để 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 thông tin bên dưới.

Hàm của mô-đun config nhận một tham số boolean không bắt buộc là flag, được đặt thành false theo mặc định. Nếu flag được đặt thành true, thì người dùng cũng như người viết quy tắc có thể đặt chế độ cài đặt bản dựng trên dòng lệnh thông qua các giá trị và quá trình chuyển đổimặc định. Không phải tất cả chế độ cài đặt đều có thể được người dùng đặt. Ví dụ: nếu bạn là người viết quy tắc 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 cấp cho người dùng khả năng bật tính năng đó một cách tuỳ tiệ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ó 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ỉ có sẵn cho ctx các đối tượng 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ị 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 triển khai chế độ cài đặt bản dựng thuộc loại 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ó tham số allow_multiple bổ sung cho phép cờ được đặt 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 thuộc tính thuộc loại 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ị:

$ bazel build //my/target --//example:roasts=blonde \
    --//example:roasts=medium,dark

Giá trị 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ể 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 bắt buộc ngầm ẩn build_setting_default Thuộc tính này có cùng loại với loại được khai báo bởi tham số build_setting.

# 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

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 được xác định trước mà bạn có thể tạo thực thể mà không cần 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 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 Quy tắc cài đặt bản dựng phổ biến.

Sử dụng chế độ cài đặt bản dựng

Phụ 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 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 tập hợp chính tắc các chế độ cài đặt bản dựng 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 đối tượng được mã hoá cứng trong thế giới cấu hình Starlark, nhưng một cách để chuyển đổi khái niệm này là sử dụng 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ác cờ gốc, bạn có thể sử dụng dòng lệnh để đặt 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 bí danh chế độ cài đặt bản dựng

Bạn có thể đặt bí danh cho đường dẫn mục tiêu 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 kép.

Đặt bí danh bằng cách thêm --flag_alias=ALIAS_NAME=TARGET_PATH vào .bazelrc . 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 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à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ù bạn có thể đặt bí danh trên dòng lệnh, nhưng việc để bí danh trong .bazelrc sẽ giúp giảm bớt sự lộn xộn của dòng lệnh.

Chế độ cài đặt bản dựng thuộc loại 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 chế độ cài đặt thuộc 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 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_flaglabel_setting có thể được đọc/ghi bởi quá trình chuyển đổi và label_flag có thể được đặt bởi người dùng như các quy tắc build_setting khác. Điểm khác biệt duy nhất là bạn không thể xác định các quy tắc này theo cách tuỳ chỉnh.

Chế độ cài đặt thuộc loại nhãn sẽ thay thế chức năng của các giá trị mặc định liên kết muộn. Thuộc tính mặc định liên kết muộn là các thuộc tính thuộc loại Nhãn có giá trị cuối cùng có thể bị ảnh hưởng bởi cấu hình. Trong Starlark, thuộc tính này sẽ thay thế configuration_field API.

# 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"
)

Chế độ cài đặt bản dựng và select()

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 chế độ cài đặt bản dựng bằng cách sử dụng select(). Bạn có thể truyền 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 a String sau đó được phân tích cú pháp thành loại chế độ cài đặt bản dựng để khớp.

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

Quá trình chuyển đổi do người dùng xác định

Quá trình chuyển đổi cấu hình á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 các 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ẹ" được xử lý bằng quá trình chuyển đổi.

Về mặt chí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 quy định hạn chế đặc biệt.

Trong Starlark, quá trình chuyển đổi được xác định giống như các quy tắc, với hàm transition() xác định và 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ó 2 tham số là settingsattr. 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 cho transition().

attr là một từ điển 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 quá trình chuyển đổi cạnh đi, các giá trị của 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 quá trình chuyển đổi cạnh đến, attr 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 quá trình 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 quá trình chuyển đổi cạnh đến sẽ đọc giá trị sai 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 quá trình chuyển đổi có nhiều cấu hình đầu ra) về các giá trị cài đặt bản dựng mới để áp dụng. Tập hợp 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 outputs tham số 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 bản dựng phải được truyền 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

Quá trình chuyển đổi cạnh đi có thể ánh xạ một cấu hình đầu vào duy nhất thành 2 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 gói mã đa kiến trúc.

Quá trình chuyển đổi 1:2+ được xác định bằng cách trả về 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 quá trình chuyển đổi

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

Bạn có thể đính kèm quá trình chuyển đổi ở 2 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 (quá trình chuyển đổi cạnh đến ) và chuyển đổi cấu hình của các phần phụ thuộc (quá trình chuyển đổi cạnh đi ).

LƯU Ý: Hiện không có cách nào để đính kèm quá trình chuyển đổi Starlark vào các quy tắc gốc. Nếu bạn cần thực hiện 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ế.

Quá trình chuyển đổi cạnh đến

Quá trình 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ố rule()'s cfg:

# example/rules.bzl
load("example/transitions:transitions.bzl", "hot_chocolate_transition")
drink_rule = rule(
    implementation = _impl,
    cfg = hot_chocolate_transition,
    ...

Quá trình chuyển đổi cạnh đến phải là quá trình chuyển đổi 1:1.

Quá trình chuyển đổi cạnh đi

Quá trình 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)}
    ...

Quá trình chuyển đổi cạnh đ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 quá trình chuyển đổi để biết cách đọc các khoá này.

Quá trình chuyển đổi trên các tuỳ chọn gốc

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

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 tuỳ chọn cấu hình bản dựng gốc thông qua 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"]

Các tuỳ 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 . Nói chung, bạn không nên sử dụng --define theo cách mới mà nên sử dụng chế độ cài đặt bản dựng.

Bazel không hỗ trợ chuyển đổi trên --config. Nguyên nhân là vì --config là cờ "mở rộng" mở rộng thành 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 để áp dụng các cờ đó trong quá trình chuyển đổi.

Để giải quyết vấn đề này, bạn có thể liệt kê rõ ràng các cờ một phần của cấu hình trong quá trình chuyển đổi. Việc này đòi hỏi phải duy trì quá trình mở rộng của --config's ở 2 vị trí, đây là một lỗi giao diện người dùng đã biết.

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

Khi đặt 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"]
)

Quá trình 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. Đ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 thành 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 quá trình chuyển đổi

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

Khi đính kèm quá trình chuyển đổi vào cạnh đi (bất kể quá trình chuyển đổi là quá trình 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 là danh sách. 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ể sử dụng ctx.split_attr để đọc các phần phụ thuộc riêng lẻ cho mỗi 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ụ hoàn chỉnh 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 có liên quan đến việc phân giải chuỗi công cụ. Trong tương lai, quá trình chuyển đổi rõ ràng trên các 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 quá trình chuyển đổi và do đó, thêm cấu hình mới vào bản dựng sẽ tốn kém: biểu đồ bản dựng lớn hơn, biểu đồ 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 cân nhắc sử dụng quá trình chuyển đổi trong quy tắc 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 hoạt động kém: một nghiên cứu điển hình

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

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ục tiêu cấp cao nhất là //pkg:app, phụ thuộc vào 2 mục tiêu là a //pkg:1_0//pkg:1_1. Cả 2 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ả 2 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 tiếp tục cho đến //pkg:n_0//pkg:n_1, cả 2 đều phụ thuộc vào một mục tiêu duy nhất là //pkg:dep.

Việc xây dựng //pkg:app đòi hỏi \(2n+2\) các mục tiêu:

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

Hãy tưởng tượng bạn triển khai 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 nối b vào giá trị cũ của --owner cho tất cả các phần phụ thuộc của nó.

Điều này 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 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\) in \(\{0,1\}\). \(2^n\)

Điều này khiến biểu đồ bản dựng lớn hơn theo cấp số nhân so với biểu đồ mục tiêu, với các hậu quả tương ứng về bộ nhớ và hiệu suất.

CẦN LÀM: Thêm các chiến lược để đo lường và giảm thiểu các 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: