การกำหนดค่า

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

# 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\)" สำหรับin \(\{0,1\}\) \(b_i\)

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

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

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

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