การกำหนดค่า

หน้านี้ครอบคลุมถึงประโยชน์และการใช้งานพื้นฐานของการกำหนดค่า 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()

การตั้งค่าการสร้างเป็นกฎเหมือนกับกฎอื่นๆ และแยกความแตกต่างโดยใช้ แอตทริบิวต์ของฟังก์ชัน rule() ของ Starlark build_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)
)

การกำหนดแฟล็กสตริงแบบหลายชุด

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

การสร้างอินสแตนซ์การตั้งค่าการสร้าง

กฎที่กำหนดด้วยพ0/} พารามิเตอร์มีแอตทริบิวต์ ที่จำเป็นโดยนัย build_setting_default build_setting แอตทริบิวต์นี้มีประเภทเดียวกับที่ประกาศโดยพารามิเตอร์ 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"
    }
)

ทรานซิชันที่ผู้ใช้กำหนด

ทรานซิชันการกำหนดค่า จะแมปการเปลี่ยนแปลงจากเป้าหมายที่กำหนดค่าหนึ่งไปยังอีกเป้าหมายหนึ่งภายใน กราฟการสร้าง

การกำหนด

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

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

ทรานซิชันขอบขาเข้า

ทรานซิชันขอบขาเข้าจะเปิดใช้งานโดยการแนบออบเจ็กต์ transition (สร้างโดย transition()) กับพารามิเตอร์ rule()'s cfg

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

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

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

การสร้างที่มีพฤติกรรมไม่ดี: กรณีศึกษา

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

รูปที่ 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 \(b_i\) ทั้งหมด \(\{0,1\}\).

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

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

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

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