หน้านี้ครอบคลุมประโยชน์และการใช้งานพื้นฐานของการกำหนดค่า 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
ที่เผยให้เห็นช่องโหว่ บิลด์ที่ผู้ใช้กำหนด
มีการกำหนดไว้ในไฟล์ .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
เพิ่มเติมซึ่งทำให้ฟังก์ชัน
ตั้งค่าสถานะหลายครั้งในบรรทัดคำสั่งหรือใน 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)
การใช้การตั้งค่าการสร้างในบรรทัดคำสั่ง
คุณสามารถใช้บรรทัดคำสั่งเพื่อตั้งค่าการสร้างที่มีการทำเครื่องหมายเป็น 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"
)
การเพิ่มทรานซิชันจะช่วยให้คุณขยายขนาดของกราฟการบิลด์ได้ง่ายๆ การดำเนินการนี้จะตั้งค่ารายการที่อนุญาตในแพ็กเกจที่คุณมีสิทธิ์ สร้างเป้าหมายของกฎนี้ได้ ค่าเริ่มต้นใน Codeblock ด้านบน อนุญาตทุกสิ่ง แต่หากคุณต้องการจำกัด ว่าใครจะใช้กฎของคุณ คุณจะตั้งค่าให้แอตทริบิวต์ดังกล่าวชี้ไปยังรายการที่อนุญาตที่คุณกำหนดเองได้ ติดต่อ bazel-discuss@googlegroups.com หากต้องการคำแนะนำหรือความช่วยเหลือ ทำความเข้าใจว่าการเปลี่ยนผ่าน ส่งผลต่อประสิทธิภาพบิลด์อย่างไร
การให้คำจำกัดความ
การเปลี่ยนกำหนดการเปลี่ยนแปลงการกำหนดค่าระหว่างกฎ เช่น คำขอ เช่น "คอมไพล์ทรัพยากร 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
จะไม่
รวมแอตทริบิวต์ที่ใช้ตัวเลือกเพื่อแก้ไขค่า หากการเปลี่ยนผ่านขอบขาเข้าใน --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 ที่ ได้แก่ ขอบขาเข้าและขอบขาออก อย่างมีประสิทธิภาพหมายความว่ากฎสามารถเปลี่ยนการกำหนดค่าของตนเอง (เข้ามา EDGE) และเปลี่ยนการขึ้นต่อกันของ การกำหนดค่า (ขาออก การเปลี่ยนขอบ)
หมายเหตุ: ปัจจุบันยังไม่มีวิธีแนบการเปลี่ยนผ่าน 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,
...
การเปลี่ยน 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
การออกแบบของ 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
เพื่ออ่าน deps แต่ละรายการสำหรับแต่ละคีย์ได้ ดังนี้
# 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
ปัจจุบัน Flag เดิมจำนวนมาก เช่น --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
สำหรับ 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\}\)
ซึ่งทำให้กราฟบิลด์มีขนาดใหญ่กว่ากราฟเป้าหมายแบบทวีคูณ หน่วยความจำและประสิทธิภาพที่เกี่ยวข้องกัน
TODO: เพิ่มกลยุทธ์ในการวัดผลและบรรเทาปัญหาเหล่านี้
อ่านเพิ่มเติม
ดูรายละเอียดเพิ่มเติมเกี่ยวกับการแก้ไขการกำหนดค่าบิลด์ได้ที่
- การกำหนดค่าบิลด์ของ Starlark
- แผนกลยุทธ์ด้านความสามารถในการกำหนดค่าของ Bazel
- ตัวอย่างแบบต้นทางถึงปลายทางทั้งชุด