หน้านี้กล่าวถึงประโยชน์และการใช้งานพื้นฐานของการกำหนดค่า 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/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",
)
โปรดดูรายการที่สมบูรณ์ที่หัวข้อกฎการตั้งค่าการสร้างทั่วไป
การใช้การตั้งค่าบิลด์
ขึ้นอยู่กับการตั้งค่าบิลด์
หากเป้าหมายต้องการอ่านข้อมูลการกําหนดค่า เป้าหมายจะขึ้นอยู่กับการตั้งค่าบิลด์โดยตรงผ่านข้อกําหนดของแอตทริบิวต์ปกติ
# 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
ด้วย ก็มีโอกาสที่การเปลี่ยนผ่านขอบขาเข้าจะอ่านค่า 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 ดังกล่าวกับแต่ละเป้าหมายตามการออกแบบ ซึ่งหมายความว่าไม่มีวิธีใช้ที่สอดคล้องกับการเปลี่ยน
วิธีแก้ปัญหาชั่วคราวคือคุณสามารถระบุ 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)
})
ดูตัวอย่างที่สมบูรณ์ที่นี่
การผสานรวมกับแพลตฟอร์มและเครื่องมือ
ปัจจุบัน Flag เดิมจำนวนมาก เช่น --cpu
และ --crosstool_top
เกี่ยวข้องกับความละเอียดของ Toolchain ในอนาคต การเปลี่ยนสถานะอย่างชัดเจนใน Flag ประเภทเหล่านี้มีแนวโน้มที่จะเปลี่ยนไปใช้การเปลี่ยนสถานะในแพลตฟอร์มเป้าหมาย
ข้อควรพิจารณาเกี่ยวกับหน่วยความจำและประสิทธิภาพ
การเพิ่มทรานซิชันและการกำหนดค่าใหม่ลงในบิลด์จะส่งผลเสียดังนี้ กราฟบิลด์มีขนาดใหญ่ขึ้น กราฟบิลด์เข้าใจยากขึ้น และบิลด์ช้าลง คุณควรคำนึงถึงค่าใช้จ่ายเหล่านี้เมื่อพิจารณาใช้การเปลี่ยนในกฎการสร้าง ด้านล่างนี้คือตัวอย่างวิธีที่การเปลี่ยนอาจทําให้กราฟการสร้างเติบโตแบบทวีคูณ
กรณีศึกษาเกี่ยวกับบิลด์ที่ทำงานไม่ถูกต้อง
รูปที่ 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\) ใน \([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\}\)
ซึ่งทําให้กราฟการสร้างมีขนาดใหญ่กว่ากราฟเป้าหมายหลายเท่า ส่งผลต่อหน่วยความจําและประสิทธิภาพ
สิ่งที่ต้องทำ: เพิ่มกลยุทธ์สำหรับการวัดผลและบรรเทาปัญหาเหล่านี้
อ่านเพิ่มเติม
ดูรายละเอียดเพิ่มเติมเกี่ยวกับการแก้ไขการกำหนดค่าบิลด์ได้ที่
- การกำหนดค่าการสร้าง Starlark
- โรดแมปการกำหนดค่า Bazel
- ชุดตัวอย่างแบบครบวงจร