การกำหนดค่า

รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

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

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

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

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

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

การกำหนดการตั้งค่าการสร้าง

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

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

การตั้งค่าบิลด์เป็นกฎเช่นเดียวกับกฎอื่นๆ และแยกความแตกต่างโดยใช้แอตทริบิวต์ของrule()ฟังก์ชัน Starlarkbuild_setting

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

การกําหนด Flag สตริงแบบหลายชุด

การตั้งค่าสตริงมีพารามิเตอร์ allow_multiple เพิ่มเติมซึ่งทำให้ฟังก์ชัน ตั้งค่าสถานะหลายครั้งในบรรทัดคำสั่งหรือใน bazelrcs ค่าเริ่มต้นของหน่วยโฆษณา ยังคงกำหนดด้วยแอตทริบิวต์ประเภทสตริง:

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

การตั้งค่า 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/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",
)

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

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

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

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

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

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

คุณสามารถใช้บรรทัดคำสั่งเพื่อตั้งค่าการสร้างที่มีการทำเครื่องหมายเป็น Flag ได้เช่นเดียวกับ Flag เนทีฟส่วนใหญ่ ชื่อการตั้งค่าการสร้างคือเส้นทางเป้าหมายแบบสมบูรณ์โดยใช้ไวยากรณ์ 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 ของภาษาและยังใช้ไวยากรณ์ตัวเลือกขีดกลาง 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"
    }
)

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

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

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

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

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

การให้คำจำกัดความ

การเปลี่ยนกำหนดการเปลี่ยนแปลงการกำหนดค่าระหว่างกฎ เช่น คำขอ เช่น "คอมไพล์ทรัพยากร Dependency สำหรับ CPU คนละตัวกับระดับบนสุด" จัดการโดย การเปลี่ยนแปลง

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

หมายเหตุ: ปัจจุบันยังไม่มีวิธีแนบการเปลี่ยนผ่าน 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,
    ...

การเปลี่ยน EDGE ขาเข้าต้องเป็นการเปลี่ยนแบบ 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 อาจรวม Flag ที่ไม่ส่งผลต่อการกำหนดค่าบิลด์ เช่น --spawn_strategy การออกแบบของ Bazel ทำให้ไม่สามารถเชื่อมโยง Flag ดังกล่าวกับเป้าหมายแต่ละรายการได้ ซึ่งหมายความว่า ไม่มีวิธีที่สอดคล้องกันในการนำไปใช้ในการเปลี่ยนแปลง

วิธีแก้ปัญหาชั่วคราวคือคุณสามารถระบุ Flag ที่เป็นส่วนหนึ่งของการกําหนดค่าในการเปลี่ยนผ่านอย่างชัดเจน ซึ่งต้องคงการขยายตัวของ --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 เพื่ออ่าน deps แต่ละรายการสำหรับแต่ละคีย์ได้ ดังนี้

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

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

การผสานรวมกับแพลตฟอร์มและ Toolchain

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

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

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

กรณีศึกษาเกี่ยวกับบิลด์ที่ทำงานไม่ถูกต้อง

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

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

กราฟนี้แสดงเป้าหมายระดับบนสุด ซึ่งก็คือ //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 สำหรับ deps ทั้งหมด

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

//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: เพิ่มกลยุทธ์ในการวัดผลและบรรเทาปัญหาเหล่านี้

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

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