การกำหนดค่า

หน้านี้ครอบคลุมถึงประโยชน์และการใช้งานพื้นฐานของการกำหนดค่า 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_setting ของฟังก์ชัน rule() ของ Starlark

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

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

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

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

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

$ 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/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

การใช้นามแฝงของการตั้งค่าบิลด์

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

ตั้งอีเมลแทนโดยเพิ่ม --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 การตั้งค่านี้จะแทนที่ 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"
)

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

ตัวอย่างแบบครบวงจร

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

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

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

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

การกำหนด

การเปลี่ยนจะกำหนดการเปลี่ยนแปลงการกำหนดค่าระหว่างกฎ ตัวอย่างเช่น การเปลี่ยนจะจัดการคำขอ เช่น "คอมไพล์ทรัพยากร Dependency ของฉันสำหรับ 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 คือพจนานุกรมของแอตทริบิวต์และค่าของกฎที่แนบการเปลี่ยนไว้ เมื่อแนบเป็นการเปลี่ยนขอบขาออก ค่าของแอตทริบิวต์เหล่านี้จะได้รับการกำหนดค่าทั้งหมดหลังจากการแก้ปัญหา 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"]
)

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

# 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()) กับพารามิเตอร์ rule()'s cfg

# 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 เป็นรายการ หากยังไม่ได้เป็น ลำดับขององค์ประกอบในรายการนี้ไม่ได้ระบุไว้

# 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\) in \([1..n]\)

ลองนึกภาพว่าคุณ ใช้ แฟล็ก --//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\) in \(\{0,1\}\).

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

สิ่งที่ต้องทำ: เพิ่มกลยุทธ์สำหรับการวัดผลและการลดปัญหาเหล่านี้

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

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