การกำหนดค่า

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

# example/buildsettings/build_settings.bzl
allow_multiple_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True, allow_multiple = True)
)
# example/buildsettings/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/buildsettings/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)

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

คุณสามารถใช้บรรทัดคำสั่งเพื่อตั้งค่าการสร้างที่มีการทำเครื่องหมายว่าเป็น 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"
  )

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

การกําหนด

การเปลี่ยนกำหนดการเปลี่ยนแปลงการกำหนดค่าระหว่างกฎ เช่น คำขออย่าง "คอมไพล์ข้อกำหนดของฉันสำหรับ 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 ด้วย ก็มีโอกาสที่การเปลี่ยน 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 ที่ ได้แก่ ขอบขาเข้าและขอบขาออก ซึ่งหมายความว่ากฎสามารถเปลี่ยนการกำหนดค่าของตนเอง (การเปลี่ยนผ่านขอบขาเข้า) และเปลี่ยนการกำหนดค่าของรายการที่ตนพึ่งพา (การเปลี่ยนผ่านขอบขาออก)

หมายเหตุ: ปัจจุบันยังไม่มีวิธีแนบการเปลี่ยนผ่าน 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 อาจรวม Flag ที่ไม่ส่งผลต่อการกำหนดค่าบิลด์ เช่น --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",
    ]
)

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

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

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

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

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

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

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

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

กราฟนี้แสดงเป้าหมายระดับบนสุด ซึ่งก็คือ //pkg:app ซึ่งขึ้นอยู่กับสองเป้าหมาย คือ //pkg:1_0 และ //pkg:1_1 เป้าหมายทั้งสองนี้ขึ้นอยู่กับ 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\}\)

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

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

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

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