การกำหนดค่า

รายงานปัญหา ดูแหล่งที่มา Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

หน้านี้จะกล่าวถึงประโยชน์และการใช้งานพื้นฐานของการกำหนดค่า Starlark API ของ Bazel สำหรับการปรับแต่งวิธีที่โปรเจ็กต์ของคุณสร้าง ซึ่งรวมถึงวิธีระบุ การตั้งค่าบิลด์และมีตัวอย่างให้ดู

ซึ่งจะช่วยให้คุณทำสิ่งต่อไปนี้ได้

  • กำหนดฟีเจอร์แฟล็กที่กำหนดเองสำหรับโปรเจ็กต์ของคุณ ซึ่งจะทำให้ไม่จำเป็นต้องใช้ --define
  • เขียนการเปลี่ยนเพื่อกำหนดค่าการขึ้นต่อกันในการกำหนดค่าที่แตกต่างจากองค์กรระดับบนสุด (เช่น --compilation_mode=opt หรือ --cpu=arm)
  • ฝังค่าเริ่มต้นที่ดีขึ้นในกฎ (เช่น สร้าง //my:android_app โดยอัตโนมัติด้วย SDK ที่ระบุ)

และอื่นๆ ทั้งหมดมาจากไฟล์ .bzl (ไม่ต้องใช้ Bazel รุ่นที่เผยแพร่) ดูตัวอย่างได้ที่ bazelbuild/examplesที่เก็บ

การตั้งค่าบิลด์ที่ผู้ใช้กำหนด

การตั้งค่าบิลด์คือข้อมูลการกำหนดค่า ชิ้นเดียว การกำหนดค่าเป็นเหมือนแมปคีย์/ค่า การตั้งค่า --cpu=ppc และ --copt="-DFoo" จะสร้างการกำหนดค่าที่มีลักษณะดังนี้ {cpu: ppc, copt: "-DFoo"} แต่ละรายการคือการตั้งค่าบิลด์

แฟล็กแบบเดิม เช่น cpu และ copt เป็นการตั้งค่าดั้งเดิม ซึ่งมีการกำหนดคีย์และตั้งค่าภายในโค้ด Java ของ Bazel ดั้งเดิม ผู้ใช้ Bazel จะอ่านและเขียนได้ผ่านทางบรรทัดคำสั่ง และ API อื่นๆ ที่ดูแลรักษาแบบเนทีฟเท่านั้น การเปลี่ยนแฟล็กเนทีฟและ API ที่แสดงแฟล็กเหล่านั้นต้องใช้การเผยแพร่ Bazel การตั้งค่าบิลด์ที่ผู้ใช้กำหนด จะกำหนดไว้ในไฟล์ .bzl (จึงไม่จำเป็นต้องมีรุ่น Bazel เพื่อ ลงทะเบียนการเปลี่ยนแปลง) นอกจากนี้ยังตั้งค่าผ่านบรรทัดคำสั่งได้ด้วย (หากมีการกำหนดเป็น flags โปรดดูข้อมูลเพิ่มเติมด้านล่าง) แต่ก็ตั้งค่าผ่านทรานซิชันที่ผู้ใช้กำหนดได้เช่นกัน

การกำหนดการตั้งค่าบิลด์

ตัวอย่างตั้งแต่ต้นจนจบ

พารามิเตอร์ build_setting rule()

การตั้งค่าบิลด์คือกฎเช่นเดียวกับกฎอื่นๆ และจะแตกต่างกันโดยใช้build_settingrule()ฟังก์ชัน Starlark แอตทริบิวต์

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

แอตทริบิวต์ build_setting จะใช้ฟังก์ชันที่กำหนดประเภทของ การตั้งค่าบิลด์ โดยประเภทจะจำกัดไว้ที่ชุดประเภท Starlark พื้นฐาน เช่น bool และ string ดูรายละเอียดได้ที่configโมดูล เอกสารประกอบ การพิมพ์ที่ซับซ้อนกว่านี้สามารถทำได้ในฟังก์ชันการใช้งานของกฎ ดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ที่ด้านล่าง

ฟังก์ชันของโมดูล config จะใช้พารามิเตอร์บูลีนที่ไม่บังคับ flag ซึ่งตั้งค่าเป็น "เท็จ" โดยค่าเริ่มต้น หากตั้งค่า flag เป็น "จริง" ผู้ใช้จะตั้งค่าการสร้าง ในบรรทัดคำสั่งได้ รวมถึงผู้เขียนกฎจะตั้งค่าได้ภายใน ผ่านค่าเริ่มต้นและการเปลี่ยน ผู้ใช้ไม่ควรตั้งค่าบางอย่างได้ เช่น หากคุณในฐานะผู้เขียนกฎมีโหมดแก้ไขข้อบกพร่องที่ต้องการเปิดใช้ภายในกฎการทดสอบ คุณไม่ต้องการให้ผู้ใช้เปิดใช้ฟีเจอร์ดังกล่าวโดยไม่เลือก ภายในกฎอื่นๆ ที่ไม่ใช่กฎการทดสอบ

การใช้ ctx.build_setting_value

เช่นเดียวกับกฎทั้งหมด กฎการตั้งค่าบิลด์มีฟังก์ชันการใช้งาน ค่าประเภท Starlark พื้นฐานของการตั้งค่าบิลด์สามารถเข้าถึงได้ผ่านเมธอด ctx.build_setting_value วิธีนี้ใช้ได้เฉพาะกับออบเจ็กต์ ctx ของกฎการตั้งค่าบิลด์เท่านั้น วิธีการติดตั้งใช้งานเหล่านี้สามารถส่งต่อค่าการตั้งค่าบิลด์ได้โดยตรง หรือดำเนินการเพิ่มเติมกับค่าดังกล่าว เช่น การตรวจสอบประเภทหรือการสร้างโครงสร้างที่ซับซ้อนมากขึ้น วิธีใช้การตั้งค่าบิลด์ประเภท 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)
)

การกำหนดค่าสถานะสตริงแบบหลายชุด

การตั้งค่าสตริงมีallow_multipleพารามิเตอร์เพิ่มเติมที่อนุญาตให้ตั้งค่า แฟล็กหลายครั้งในบรรทัดคำสั่งหรือใน bazelrcs ระบบจะยังคงตั้งค่าเริ่มต้น ของแอตทริบิวต์ประเภทสตริงไว้

# example/buildsettings/build_settings.bzl
allow_multiple_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True, allow_multiple = True)
)
# example/buildsettings/BUILD
load("//example/buildsettings:build_settings.bzl", "allow_multiple_flag")
allow_multiple_flag(
    name = "roasts",
    build_setting_default = "medium"
)

การตั้งค่าแต่ละรายการของ Flag จะถือเป็นค่าเดียว

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

ระบบจะแยกวิเคราะห์ข้อความข้างต้นเป็น {"//example:roasts": ["blonde", "medium,dark"]} และ ctx.build_setting_value จะแสดงผลรายการ ["blonde", "medium,dark"]

การสร้างอินสแตนซ์ของการตั้งค่าบิลด์

กฎที่กำหนดด้วยพารามิเตอร์ build_setting จะมีแอตทริบิวต์ build_setting_default ที่ต้องระบุโดยนัย แอตทริบิวต์นี้มีประเภทเดียวกันกับที่ประกาศโดยพารามิเตอร์ 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/buildsettings/BUILD
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
    name = "favorite_flavor",
    build_setting_default = "APPLE"
)

การตั้งค่าที่กำหนดไว้ล่วงหน้า

ตัวอย่างตั้งแต่ต้นจนจบ

ไลบรารี Skylib มีชุดการตั้งค่าที่กำหนดไว้ล่วงหน้าซึ่งคุณสามารถสร้างอินสแตนซ์ได้โดยไม่ต้องเขียน Starlark ที่กำหนดเอง

เช่น หากต้องการกำหนดการตั้งค่าที่ยอมรับชุดค่าสตริงที่จำกัด ให้ทำดังนี้

# example/BUILD
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
string_flag(
    name = "myflag",
    values = ["a", "b", "c"],
    build_setting_default = "a",
)

ดูรายการทั้งหมดได้ที่กฎการตั้งค่าบิลด์ทั่วไป

การใช้การตั้งค่าบิลด์

ขึ้นอยู่กับการตั้งค่าบิลด์

หากเป้าหมายต้องการอ่านข้อมูลการกำหนดค่า ระบบจะ ขึ้นอยู่กับการตั้งค่าบิลด์โดยตรงผ่านการขึ้นต่อกันของแอตทริบิวต์ปกติ

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

ภาษาอาจต้องการสร้างชุดการตั้งค่าบิลด์ที่ชัดเจนซึ่งกฎทั้งหมด สำหรับภาษานั้นขึ้นอยู่กับชุดการตั้งค่านี้ แม้ว่าแนวคิดดั้งเดิมของ fragments จะไม่มีอยู่อีกต่อไป ในฐานะออบเจ็กต์ที่ฮาร์ดโค้ดในโลกการกำหนดค่า Starlark แต่ วิธีหนึ่งในการแปลแนวคิดนี้คือการใช้ชุดแอตทริบิวต์โดยนัยทั่วไป เช่น

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

การใช้การตั้งค่าบิลด์ในบรรทัดคำสั่ง

คุณใช้บรรทัดคำสั่งเพื่อตั้งค่าบิลด์ ที่ทำเครื่องหมายเป็นแฟล็กได้เช่นเดียวกับแฟล็กเนทีฟส่วนใหญ่ ชื่อของ การตั้งค่าบิลด์คือเส้นทางเป้าหมายแบบเต็มโดยใช้ไวยากรณ์ name=value

$ bazel build //my/target --//example:string_flag=some-value # allowed
$ bazel build //my/target --//example:string_flag some-value # not allowed

ระบบรองรับไวยากรณ์บูลีนพิเศษดังนี้

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

การใช้ชื่อแทนการตั้งค่าบิลด์

คุณตั้งค่าชื่อแทนสำหรับเส้นทางเป้าหมายของการตั้งค่าบิลด์เพื่อให้ อ่านได้ง่ายขึ้นในบรรทัดคำสั่ง นามแฝงทำงานคล้ายกับแฟล็กดั้งเดิมและยังใช้ไวยากรณ์ตัวเลือกขีดคู่ด้วย

ตั้งค่าชื่อแทนโดยเพิ่ม --flag_alias=ALIAS_NAME=TARGET_PATH ลงใน .bazelrc เช่น หากต้องการตั้งค่าชื่อแทนเป็น coffee ให้ทำดังนี้

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

แนวทางปฏิบัติแนะนำ: การตั้งค่าชื่อแทนหลายครั้งจะทำให้ชื่อแทนล่าสุดมีลำดับความสำคัญสูงสุด ใช้ชื่อแทนที่ไม่ซ้ำกันเพื่อหลีกเลี่ยงผลการแยกวิเคราะห์ที่ไม่ต้องการ

หากต้องการใช้นามแฝง ให้พิมพ์นามแฝงแทนเส้นทางเป้าหมายของการตั้งค่าบิลด์ จากตัวอย่างข้างต้นที่ coffee ตั้งค่าไว้ใน .bazelrc ของผู้ใช้

$ bazel build //my/target --coffee=ICED

แทนที่จะเป็น

$ bazel build //my/target --//experimental/user/starlark_configurations/basic_build_setting:coffee-temp=ICED

แนวทางปฏิบัติแนะนำ: แม้ว่าจะตั้งค่าชื่อแทนในบรรทัดคำสั่งได้ แต่การปล่อยให้ชื่อแทนอยู่ใน .bazelrc จะช่วยลดความซับซ้อนของบรรทัดคำสั่ง

การตั้งค่าบิลด์ประเภทป้ายกำกับ

ตัวอย่างตั้งแต่ต้นจนจบ

การตั้งค่าประเภทป้ายกำกับจะกำหนดโดยใช้พารามิเตอร์กฎ build_setting ไม่ได้ ซึ่งต่างจากการตั้งค่าบิลด์อื่นๆ แต่ Bazel มีกฎในตัว 2 ข้อ ได้แก่ label_flag และ label_setting กฎเหล่านี้จะส่งต่อผู้ให้บริการของเป้าหมายจริงที่มีการตั้งค่าบิลด์ label_flag และ label_setting สามารถอ่าน/เขียนได้โดยการเปลี่ยนสถานะ และผู้ใช้สามารถตั้งค่า label_flag ได้ เช่นเดียวกับกฎ build_setting อื่นๆ โดยมีความแตกต่างเพียงอย่างเดียวคือ ไม่สามารถกำหนดค่าได้

ในที่สุดการตั้งค่าประเภทป้ายกำกับจะมาแทนที่ฟังก์ชันการทำงานของค่าเริ่มต้นที่กำหนดในภายหลัง แอตทริบิวต์เริ่มต้นที่เชื่อมโยงภายหลังคือแอตทริบิวต์ประเภทป้ายกำกับซึ่งค่าสุดท้ายอาจได้รับผลกระทบจากการกำหนดค่า ใน Starlark ฟังก์ชันนี้จะมาแทนที่ 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"
)

การตั้งค่าบิลด์และ select()

ตัวอย่างตั้งแต่ต้นจนจบ

ผู้ใช้สามารถกำหนดค่าแอตทริบิวต์ในการตั้งค่าบิลด์ได้โดยใช้ select() ส่งเป้าหมายการตั้งค่าบิลด์ไปยังแอตทริบิวต์ flag_values ของ config_setting ค่าที่จะจับคู่กับการกำหนดค่าจะส่งเป็น String จากนั้นจะแยกวิเคราะห์เป็นประเภทของการตั้งค่าบิลด์เพื่อการจับคู่

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

การเปลี่ยนที่ผู้ใช้กำหนด

การเปลี่ยน การกำหนดค่า จะแมปการเปลี่ยนรูปแบบจากเป้าหมายที่กำหนดค่าหนึ่งไปยังอีกเป้าหมายหนึ่งภายใน กราฟการสร้าง

กฎที่ตั้งค่าแอตทริบิวต์เหล่านี้ต้องมีแอตทริบิวต์พิเศษดังนี้

  "_allowlist_function_transition": attr.label(
      default = "@bazel_tools//tools/allowlists/function_transition_allowlist"
  )

การเพิ่มทรานซิชันจะช่วยให้คุณขยายขนาดของกราฟการสร้างได้อย่างง่ายดาย ซึ่งจะตั้งค่ารายการที่อนุญาตในแพ็กเกจที่คุณสร้างเป้าหมายของกฎนี้ได้ ค่าเริ่มต้นในโค้ดบล็อกด้านบน อนุญาตทุกอย่าง แต่หากต้องการจำกัดผู้ที่ใช้กฎของคุณ คุณสามารถตั้งค่าแอตทริบิวต์ดังกล่าวให้ชี้ไปยังรายการที่อนุญาตที่กำหนดเองของคุณเองได้ โปรดติดต่อ bazel-discuss@googlegroups.com หากต้องการคำแนะนำหรือความช่วยเหลือ เพื่อทำความเข้าใจว่าการเปลี่ยนเฟสอาจส่งผลต่อประสิทธิภาพการบิลด์อย่างไร

การกำหนด

การเปลี่ยนผ่านจะกำหนดการเปลี่ยนแปลงการกำหนดค่าระหว่างกฎ เช่น คำขอ อย่าง "คอมไพล์การอ้างอิงของฉันสำหรับ CPU อื่นที่ไม่ใช่ CPU หลัก" จะได้รับการจัดการโดยการเปลี่ยนสถานะ

ในทางเทคนิค การเปลี่ยนสถานะคือฟังก์ชันจากการกำหนดค่าอินพุตไปยังการกำหนดค่าเอาต์พุตอย่างน้อย 1 รายการ การเปลี่ยนส่วนใหญ่จะเป็นแบบ 1:1 เช่น "ลบล้างการกำหนดค่าอินพุตด้วย --cpu=ppc" การเปลี่ยนแบบ 1:2 ขึ้นไปก็มีเช่นกัน แต่จะมีข้อจำกัดพิเศษ

ใน Starlark การเปลี่ยนสถานะจะกำหนดคล้ายกับกฎ โดยมีtransition() ฟังก์ชัน การกำหนดและฟังก์ชันการใช้งาน

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

ฟังก์ชัน transition() จะใช้ฟังก์ชันการใช้งาน ชุดการตั้งค่าบิลด์ที่จะอ่าน(inputs) และชุดการตั้งค่าบิลด์ที่จะเขียน (outputs) ฟังก์ชันการใช้งานมีพารามิเตอร์ 2 รายการ ได้แก่ settings และ attr settings คือพจนานุกรม {String:Object} ของการตั้งค่าทั้งหมดที่ประกาศ ในพารามิเตอร์ inputs ถึง transition()

attr คือพจนานุกรมของแอตทริบิวต์และค่าของกฎที่แนบการเปลี่ยนผ่าน เมื่อแนบเป็นการเปลี่ยนขอบขาออก ค่าของแอตทริบิวต์เหล่านี้ทั้งหมดจะได้รับการกำหนดค่าหลังจากการแก้ปัญหา post-select() เมื่อแนบเป็นการเปลี่ยนขอบขาเข้า attr จะไม่รวมแอตทริบิวต์ใดๆ ที่ใช้ตัวเลือกเพื่อแก้ค่า หากการเปลี่ยนขอบขาเข้าใน --foo อ่านแอตทริบิวต์ bar แล้วเลือกใน --foo เพื่อตั้งค่าแอตทริบิวต์ bar ก็อาจทำให้การเปลี่ยนขอบขาเข้าอ่านค่า bar ที่ไม่ถูกต้องในการเปลี่ยน

ฟังก์ชันการติดตั้งใช้งานต้องแสดงผลพจนานุกรม (หรือรายการพจนานุกรมในกรณีของการเปลี่ยนที่มีการกำหนดค่าเอาต์พุตหลายรายการ) ของค่าการตั้งค่าการบิลด์ใหม่ที่จะใช้ ชุดคีย์พจนานุกรมที่ส่งคืนต้อง มีชุดการตั้งค่าบิลด์ที่ส่งไปยังพารามิเตอร์ outputs ของฟังก์ชันการเปลี่ยนอย่างแน่นอน เหตุการณ์เช่นนี้เกิดขึ้นได้เสมอแม้ว่าจะไม่ได้เปลี่ยนการตั้งค่าบิลด์ในระหว่างการเปลี่ยนจริงๆ แต่ค่าเดิมจะต้องส่งผ่านอย่างชัดเจนในพจนานุกรมที่ส่งคืน

การกำหนดการเปลี่ยน 1:2 ขึ้นไป

ตัวอย่างตั้งแต่ต้นจนจบ

การเปลี่ยนขอบขาออกสามารถแมปการกำหนดค่าอินพุตเดียวกับการกำหนดค่าเอาต์พุต 2 รายการขึ้นไป ซึ่งมีประโยชน์ในการกำหนด กฎที่รวมโค้ดแบบหลายสถาปัตยกรรม

การเปลี่ยนฉาก 1:2+ จะกำหนดโดยการแสดงผลรายการพจนานุกรมใน ฟังก์ชันการติดตั้งใช้งานการเปลี่ยนฉาก

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

นอกจากนี้ยังตั้งค่าคีย์ที่กำหนดเองซึ่งฟังก์ชันการใช้งานกฎสามารถใช้เพื่อ อ่านการอ้างอิงแต่ละรายการได้ด้วย

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

การแนบทรานซิชัน

ตัวอย่างตั้งแต่ต้นจนจบ

คุณแนบทรานซิชันได้ 2 ที่ ได้แก่ ขอบขาเข้าและขอบขาออก ซึ่งหมายความว่ากฎสามารถเปลี่ยนการกำหนดค่าของตัวเอง (การเปลี่ยนขอบขาเข้า) และเปลี่ยนการกำหนดค่าของทรัพยากร Dependency (การเปลี่ยนขอบขาออก)

หมายเหตุ: ขณะนี้ยังไม่มีวิธีแนบการเปลี่ยน Starlark กับกฎดั้งเดิม หากต้องการดำเนินการนี้ โปรดติดต่อ bazel-discuss@googlegroups.com เพื่อขอความช่วยเหลือในการหาวิธีแก้ปัญหา

การเปลี่ยนขอบขาเข้า

การเปลี่ยนฉากขาเข้าจะเปิดใช้งานโดยการแนบออบเจ็กต์ transition (สร้างโดย transition()) ไปยังพารามิเตอร์ cfg ของ rule()

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

การเปลี่ยนฉากที่เข้ามาต้องเป็นการเปลี่ยนฉากแบบ 1:1

การเปลี่ยนขอบขาออก

การเปลี่ยนขอบขาออกจะเปิดใช้งานโดยการแนบออบเจ็กต์ transition (สร้างโดย transition()) ไปยังพารามิเตอร์ cfg ของแอตทริบิวต์

# example/rules.bzl
load("example/transitions:transitions.bzl", "coffee_transition")
drink_rule = rule(
    implementation = _impl,
    attrs = { "dep": attr.label(cfg = coffee_transition)}
    ...

การเปลี่ยนฉากขาออกอาจเป็น 1:1 หรือ 1:2 ขึ้นไป

ดูวิธีอ่านคีย์เหล่านี้ได้ที่การเข้าถึงแอตทริบิวต์ด้วยการเปลี่ยนผ่าน

การเปลี่ยนในตัวเลือกเนทีฟ

ตัวอย่างตั้งแต่ต้นจนจบ

การเปลี่ยนสถานะ Starlark ยังประกาศการอ่านและการเขียนในตัวเลือกการกำหนดค่าการบิลด์ดั้งเดิมได้ด้วย ผ่านคำนำหน้าที่พิเศษสำหรับชื่อตัวเลือก

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

ตัวเลือกโฆษณาเนทีฟที่ไม่รองรับ

Bazel ไม่รองรับการเปลี่ยนใน --define ด้วย "//command_line_option:define" แต่ให้ใช้การตั้งค่าบิลด์ที่กำหนดเองแทน โดยทั่วไป เราไม่แนะนำให้ใช้ --define ในลักษณะใหม่ๆ แต่แนะนำให้ใช้การตั้งค่าบิลด์แทน

Bazel ไม่รองรับการเปลี่ยนสถานะใน --config เนื่องจาก --config เป็น แฟล็ก "การขยาย" ที่ขยายไปยังแฟล็กอื่นๆ

ที่สำคัญ --config อาจมีแฟล็กที่ไม่มีผลต่อการกำหนดค่าบิลด์ เช่น --spawn_strategy Bazel ออกแบบมาให้ไม่สามารถเชื่อมโยงแฟล็กดังกล่าวกับเป้าหมายแต่ละรายการได้ ซึ่งหมายความว่า ไม่มีวิธีที่สอดคล้องกันในการใช้ในทรานซิชัน

คุณสามารถระบุรายการแฟล็กที่เป็นส่วนหนึ่งของการกำหนดค่าในการเปลี่ยนผ่านได้อย่างชัดเจนเพื่อเป็นวิธีแก้ปัญหาชั่วคราว ซึ่งต้องดูแลการขยายของ --config ใน 2 ที่ ซึ่งเป็นข้อบกพร่องที่ทราบกันดีใน UI

การเปลี่ยนผ่านในการอนุญาตการตั้งค่าบิลด์หลายรายการ

เมื่อตั้งค่าการสร้างที่อนุญาตหลายค่า คุณต้องตั้งค่าของ การตั้งค่าด้วยรายการ

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

การเปลี่ยนที่ไม่มีการดำเนินการ

หากการเปลี่ยนค่าแสดงผลเป็น {}, [] หรือ None แสดงว่าเป็นการย่อความหมายของการคงค่าการตั้งค่าทั้งหมดไว้ที่ค่าเดิม ซึ่งอาจสะดวกกว่าการ ตั้งค่าเอาต์พุตแต่ละรายการเป็นตัวมันเองอย่างชัดเจน

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

การเข้าถึงแอตทริบิวต์ด้วยการเปลี่ยน

ตัวอย่างตั้งแต่ต้นจนจบ

เมื่อแนบการเปลี่ยนฉากกับขอบขาออก (ไม่ว่าการเปลี่ยนฉากจะเป็นแบบ 1:1 หรือ 1:2 ขึ้นไป) ระบบจะบังคับให้ ctx.attr เป็น list หากยังไม่ได้เป็น ลำดับขององค์ประกอบในรายการนี้ไม่ได้ระบุไว้

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

หากการเปลี่ยนสถานะเป็น 1:2+ และตั้งค่าคีย์ที่กำหนดเอง คุณจะใช้ ctx.split_attr เพื่ออ่านการขึ้นต่อกันแต่ละรายการสำหรับแต่ละคีย์ได้ โดยทำดังนี้

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

ดูตัวอย่างที่สมบูรณ์ ได้ที่นี่

การผสานรวมกับแพลตฟอร์มและชุดเครื่องมือ

ปัจจุบันฟีเจอร์หลายอย่าง เช่น --cpu และ --crosstool_top เกี่ยวข้องกับ การแก้ไข Toolchain ในอนาคต การเปลี่ยนสถานะอย่างชัดเจนในรายงานประเภทนี้ น่าจะถูกแทนที่ด้วยการเปลี่ยนสถานะในแพลตฟอร์มเป้าหมาย

ข้อควรพิจารณาเกี่ยวกับหน่วยความจำและประสิทธิภาพ

การเพิ่มทรานซิชันและดังนั้นการกำหนดค่าใหม่ๆ ลงในการสร้างจะทำให้เกิด ค่าใช้จ่าย ได้แก่ กราฟการสร้างที่ใหญ่ขึ้น กราฟการสร้างที่เข้าใจยากขึ้น และการสร้างที่ช้าลง คุณควรพิจารณาค่าใช้จ่ายเหล่านี้เมื่อพิจารณา ใช้การเปลี่ยนในกฎการสร้าง ตัวอย่างต่อไปนี้แสดงให้เห็นว่าการเปลี่ยน อาจทำให้กราฟการสร้างเติบโตแบบทวีคูณได้อย่างไร

การสร้างที่ไม่ดี: กรณีศึกษา

กราฟความสามารถในการปรับขนาด

รูปที่ 1 กราฟความสามารถในการปรับขนาดที่แสดงเป้าหมายระดับบนสุดและการขึ้นต่อกัน

กราฟนี้แสดงเป้าหมายระดับบนสุด //pkg:app ซึ่งขึ้นอยู่กับเป้าหมาย 2 รายการ ได้แก่ //pkg:1_0 และ //pkg:1_1 เป้าหมายทั้ง 2 รายการนี้ขึ้นอยู่กับเป้าหมาย 2 รายการ ได้แก่ //pkg:2_0 และ //pkg:2_1 เป้าหมายทั้ง 2 รายการนี้ขึ้นอยู่กับเป้าหมาย 2 รายการ ได้แก่ //pkg:3_0 และ //pkg:3_1 ซึ่งจะดำเนินต่อไปจนถึง //pkg:n_0 และ //pkg:n_1 ซึ่งทั้ง 2 รายการขึ้นอยู่กับเป้าหมายเดียวคือ //pkg:dep

การสร้าง//pkg:appต้อง \(2n+2\) มีเป้าหมายดังนี้

  • //pkg:app
  • //pkg:dep
  • //pkg:i_0 และ //pkg:i_1 สำหรับ \(i\) ใน \([1..n]\)

สมมติว่าคุณใช้) Flag --//foo:owner=<STRING> และ //pkg:i_b มีผล

depConfig = myConfig + depConfig.owner="$(myConfig.owner)$(b)"

กล่าวคือ //pkg:i_b จะต่อท้าย b กับค่าเดิมของ --owner สำหรับ การขึ้นต่อกันทั้งหมด

ซึ่งจะสร้างเป้าหมายที่กำหนดค่าแล้วต่อไปนี้

//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 สร้าง \(2^n\) เป้าหมายที่กำหนดค่าไว้: config.owner= "\(b_0b_1...b_n\)" สำหรับ \(b_i\) ทั้งหมดใน \(\{0,1\}\)

ซึ่งจะทำให้กราฟการสร้างมีขนาดใหญ่กว่ากราฟเป้าหมายอย่างมาก โดยมีผลกระทบต่อหน่วยความจำและประสิทธิภาพที่สอดคล้องกัน

TODO: Add strategies for measurement and mitigation of these issues.

อ่านเพิ่มเติม

ดูรายละเอียดเพิ่มเติมเกี่ยวกับการแก้ไขการกำหนดค่าบิลด์ได้ที่