การกำหนดค่า

รายงานปัญหา ดูแหล่งที่มา

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

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

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

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

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

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

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

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

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

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

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

# 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 เพิ่มเติม ซึ่งทำให้สามารถตั้งค่า Flag ได้หลายครั้งในบรรทัดคำสั่งหรือใน 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"
)

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

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

ภาษาอาจต้องการสร้างชุดการตั้งค่าบิลด์ Canonical ที่กฎทั้งหมดสำหรับภาษานั้นต้องใช้ แม้ว่าแนวคิดดั้งเดิมของ 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() สามารถส่งเป้าหมายการตั้งค่าบิลด์ไปยังแอตทริบิวต์ 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: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 เป็นพจนานุกรมของแอตทริบิวต์และค่าของกฎที่แนบมากับการเปลี่ยน เมื่อแนบเป็นการเปลี่ยนผ่าน Edge ขาออก ค่าของแอตทริบิวต์เหล่านี้จะเป็นความละเอียด Post-select() ที่กำหนดค่าไว้ทั้งหมด เมื่อแนบเป็นการเปลี่ยนผ่าน Edge ขาเข้า attr จะไม่รวมแอตทริบิวต์ที่ใช้ตัวเลือกเพื่อแก้ไขค่า หากการเปลี่ยนผ่าน Edge ขาเข้าใน --foo อ่านแอตทริบิวต์ bar แล้วเลือกใน --foo เพื่อตั้งค่าแอตทริบิวต์ bar ด้วย ก็มีโอกาสที่การเปลี่ยนผ่าน Edge ขาเข้าจะอ่านค่า bar ที่ไม่ถูกต้องในช่วงการเปลี่ยน

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

การกำหนดการเปลี่ยนแบบ 1:2+

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

การเปลี่ยนผ่าน Edge ขาออกสามารถแมปการกำหนดค่าอินพุตเดียวกับการกำหนดค่าเอาต์พุตอย่างน้อย 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 ตำแหน่ง ได้แก่ ขอบขาเข้าและขอบขาออก ซึ่งหมายความว่ากฎจะสามารถเปลี่ยนการกำหนดค่าของตนเอง (การเปลี่ยนผ่าน Edge ขาเข้า) และเปลี่ยนการกำหนดค่าของทรัพยากร Dependency (การเปลี่ยนปลายทางขาออก) ได้อย่างมีประสิทธิภาพ

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

การเปลี่ยนผ่าน Edge ขาเข้า

การเปลี่ยน Edge ขาเข้าจะเปิดใช้งานโดยการแนบออบเจ็กต์ 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

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

การเปลี่ยนผ่าน Edge ขาออกจะเปิดใช้งานโดยการแนบออบเจ็กต์ 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)}
    ...

การเปลี่ยน Edge ขาออกอาจเป็นแบบ 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 เป็น Flag "การขยาย" ที่ขยายไปยัง Flag อื่นๆ

ที่สำคัญ --config อาจรวมแฟล็กที่ไม่ส่งผลต่อการกำหนดค่าบิลด์ เช่น --spawn_strategy Bazel ออกแบบมาให้เชื่อมโยง 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",
    ]
)

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

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

เมื่อแนบการเปลี่ยนกับ Edge ขาออก (ไม่ว่าการเปลี่ยนจะเป็นแบบ 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 เพื่ออ่าน Dep แต่ละรายการสำหรับแต่ละคีย์ได้ ดังนี้

# 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 กราฟความสามารถในการปรับขนาดแสดงเป้าหมายระดับบนสุดและทรัพยากร 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]\)

ลองจินตนาการว่าคุณimplement 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\}\)

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

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

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

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