การกำหนดค่า

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

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

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

  • กำหนดแฟล็กที่กำหนดเอง สำหรับโปรเจ็กต์ของคุณ --define
  • เขียน การเปลี่ยน เพื่อกำหนดค่า Deps ใน การกำหนดค่าแตกต่างจากรายการหลัก (เช่น --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 อื่นๆ ที่ดูแลตามปกติ การเปลี่ยน Flag เนทีฟและ API ที่เผยให้เห็นช่องโหว่ บิลด์ที่ผู้ใช้กำหนด มีการกำหนดไว้ในไฟล์ .bzl ไฟล์ (ดังนั้นจึงไม่จำเป็นต้องเผยแพร่ด้วย bazel ลงทะเบียนการเปลี่ยนแปลง) นอกจากนี้ยังสามารถตั้งค่าผ่านบรรทัดคำสั่ง (หากได้รับการกำหนดให้เป็น flags โปรดดูข้อมูลเพิ่มเติมด้านล่าง) และยังสามารถเป็น ตั้งค่าผ่านการเปลี่ยนที่กำหนดโดยผู้ใช้

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

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

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

การตั้งค่าบิลด์เป็นกฎเช่นเดียวกับกฎอื่นๆ และแยกความแตกต่างโดยใช้ 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)
)

การกำหนด Flag สตริงที่มีหลายชุด

การตั้งค่าสตริงมีพารามิเตอร์ 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"
)

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

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

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

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

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

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

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

หมายเหตุ: ขณะนี้ยังไม่มีวิธีแนบการเปลี่ยนของ Starlark กับกฎแบบเนทีฟ หากคุณจำเป็นต้องดำเนินการดังกล่าว โปรดติดต่อ [email protected] เพื่อขอความช่วยเหลือในการหาวิธีแก้ไขปัญหาเบื้องต้น

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

เปิดใช้งานการเปลี่ยนขอบขาเข้าโดยการแนบออบเจ็กต์ 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 ที่ใช้เวลาเพียง 2 นาที การออกแบบของ 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",
    ]
)

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

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

เมื่อแนบการเปลี่ยนเข้ากับขอบขาออก (ไม่ว่าการเปลี่ยนจะเป็นการเปลี่ยนแบบ 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)
    })

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

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

แฟล็กเนทีฟจำนวนมากในปัจจุบัน เช่น --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 สำหรับทุกรายการ ท้องถิ่น

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

//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\}\)

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

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

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

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