หน้านี้ครอบคลุมประโยชน์และการใช้งานพื้นฐานของการกำหนดค่า Starlark, API ของ Bazel ในการปรับแต่งการสร้างโปรเจ็กต์ของคุณ ซึ่งมีวิธีกำหนดการตั้งค่าการสร้าง และให้ตัวอย่าง
วิธีนี้ช่วยให้คุณทำสิ่งต่อไปนี้ได้
- กำหนด Flag ที่กำหนดเองสำหรับโปรเจ็กต์ของคุณโดยไม่ทำให้ต้องมี
--define
- เขียนการเปลี่ยน เพื่อกำหนดค่า Dep ที่มีการกำหนดค่าที่แตกต่างจากการกำหนดค่าหลัก
(เช่น
--compilation_mode=opt
หรือ--cpu=arm
) - สร้างค่าเริ่มต้นที่ดีขึ้นให้เป็นกฎ (เช่น สร้าง
//my:android_app
ด้วย SDK ที่ระบุโดยอัตโนมัติ)
และอีกมากมาย ทั้งหมดมาจากไฟล์ .bzl (ไม่ต้องใช้รุ่น Bazel) ดูตัวอย่างในที่เก็บ bazelbuild/examples
การตั้งค่าบิลด์ที่ผู้ใช้กำหนด
การตั้งค่าบิลด์คือข้อมูลการกำหนดค่าชิ้นส่วนเดียว ให้คิดว่าการกำหนดค่าเป็นการแมปคีย์/ค่า การตั้งค่า --cpu=ppc
และ --copt="-DFoo"
จะสร้างการกำหนดค่าที่มีลักษณะดังนี้ {cpu: ppc, copt: "-DFoo"}
โดยแต่ละรายการคือการตั้งค่าบิลด์
แฟล็กแบบดั้งเดิมอย่าง cpu
และ copt
เป็นการตั้งค่าแบบเนทีฟ
ซึ่งคีย์ของตัวระบุจะได้รับการกำหนดค่าและตั้งค่าในโค้ด Bazel Java ที่มาพร้อมเครื่อง
ผู้ใช้ Bazel จะอ่านและเขียนได้โดยใช้บรรทัดคำสั่งและ API อื่นๆ ที่เก็บรักษาไว้อยู่แล้วเท่านั้น การเปลี่ยน Flag เนทีฟและ API ที่แสดงรายการดังกล่าวต้องใช้การปลดเปลื้องผ้า การตั้งค่าบิลด์ที่ผู้ใช้กำหนดจะกำหนดไว้ในไฟล์ .bzl
(ดังนั้นจึงไม่ต้องใช้รุ่น Bazel เพื่อลงทะเบียนการเปลี่ยนแปลง) นอกจากนี้ยังตั้งค่าผ่านบรรทัดคำสั่งได้ด้วย (หากกำหนดเป็น flags
โปรดดูข้อมูลเพิ่มเติมด้านล่าง) แต่ก็ตั้งค่าผ่านการเปลี่ยนที่ผู้ใช้กำหนดได้ด้วย
การกำหนดการตั้งค่าบิลด์
พารามิเตอร์ rule()
build_setting
การตั้งค่าบิลด์เป็นกฎแบบเดียวกับกฎอื่นๆ และแยกความแตกต่างโดยใช้แอตทริบิวต์ 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)
)
การกำหนด Flag สตริงที่มีหลายชุด
การตั้งค่าสตริงมีพารามิเตอร์ allow_multiple
เพิ่มเติมซึ่งอนุญาตให้ตั้งค่า Flag ได้หลายครั้งในบรรทัดคำสั่งหรือใน bazelrcs ค่าเริ่มต้นของโมเดลจะยังคงกำหนดด้วยแอตทริบิวต์ประเภทสตริง ดังนี้
# 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",
)
ดูรายการทั้งหมดได้ที่กฎการตั้งค่าบิลด์ทั่วไป
กำลังใช้การตั้งค่าบิลด์
ขึ้นอยู่กับการตั้งค่าบิลด์
หากเป้าหมายต้องการอ่านข้อมูลการกำหนดค่าชิ้นหนึ่ง เป้าหมายอาจขึ้นอยู่กับการตั้งค่าบิลด์โดยตรงผ่านทรัพยากร Dependency ของแอตทริบิวต์ปกติ
# 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",
)
ภาษาอาจต้องการสร้างชุดการตั้งค่าเวอร์ชัน Canonical ซึ่งกฎทั้งหมดสำหรับภาษานั้นขึ้นอยู่กับ แม้ว่าแนวคิดดั้งเดิมของ 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
ใช้ชื่อแทนการตั้งค่าบิลด์
คุณสามารถสร้างชื่อแทนสำหรับเส้นทางเป้าหมายในการตั้งค่าบิลด์เพื่อให้อ่านบรรทัดคำสั่งได้ง่ายขึ้น ชื่อแทนจะทำงานคล้ายกับ Flag เนทีฟและใช้ประโยชน์จากไวยากรณ์ตัวเลือกขีดกลางคู่
สร้างชื่อแทนโดยเพิ่ม --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 การดำเนินการนี้จะแทนที่ API ของ configuration_field
# 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 อื่นที่ไม่ใช่ 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
คือพจนานุกรมของแอตทริบิวต์และค่าของกฎที่มีการแนบการเปลี่ยน เมื่อแนบเป็นการเปลี่ยน EDGE ขาออก ค่าของแอตทริบิวต์เหล่านี้จะมีการกำหนดค่าความละเอียดหลังจากการเลือก() ทั้งหมด เมื่อแนบเป็นการเปลี่ยนปลายทางขาเข้า 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 ที่ ได้แก่ ขอบขาเข้าและขอบขาออก ซึ่งหมายความว่ากฎจะเปลี่ยนการกำหนดค่าของตนเองได้ (การเปลี่ยนแบบ Edge ขาเข้า) และเปลี่ยนการกำหนดค่าของทรัพยากร Dependency (การเปลี่ยน 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
อาจรวมแฟล็กที่ไม่ส่งผลต่อการกำหนดค่าบิลด์ เช่น --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)
})
ดูตัวอย่างที่สมบูรณ์ที่นี่
การผสานรวมกับแพลตฟอร์มและ Toolchain
Flag เนทีฟจำนวนมากในปัจจุบัน เช่น --cpu
และ --crosstool_top
เกี่ยวข้องกับความละเอียดของ Toolchain ในอนาคต การเปลี่ยนผ่านที่ชัดแจ้งประเภทการแจ้งว่าไม่เหมาะสมเหล่านี้น่าจะแทนที่โดยการเปลี่ยนผ่านแพลตฟอร์มเป้าหมาย
ข้อควรพิจารณาเกี่ยวกับหน่วยความจำและประสิทธิภาพ
การเพิ่มการเปลี่ยนและการกำหนดค่าใหม่ให้กับบิลด์จึงมีต้นทุนสูง เช่น กราฟบิลด์ที่ใหญ่ขึ้น กราฟบิลด์ที่เข้าใจได้น้อยลง และการสร้างที่ช้าลง คุณควรคำนึงถึงค่าใช้จ่ายเหล่านี้เมื่อพิจารณาใช้การเปลี่ยนในกฎการสร้าง ด้านล่างคือตัวอย่างที่แสดงให้เห็นว่าการเปลี่ยนผ่านอาจสร้างการเติบโตแบบทวีคูณของกราฟบิลด์
งานสร้างที่ประพฤติไม่ดี: กรณีศึกษา
รูปที่ 1 กราฟความสามารถในการปรับขนาดแสดงเป้าหมายระดับบนสุดและทรัพยากร Dependency ของเป้าหมาย
กราฟนี้แสดงเป้าหมายระดับบนสุด ซึ่งก็คือ //pkg:app ซึ่งขึ้นอยู่กับเป้าหมาย 2 รายการ ได้แก่ //pkg:1_0 และ //pkg:1_1 เป้าหมายทั้งสองนี้ขึ้นอยู่กับ 2 เป้าหมาย คือ //pkg:2_0 และ //pkg:2_1 เป้าหมายทั้งสองนี้ขึ้นอยู่กับสองเป้าหมาย คือ //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]\)
สมมติว่าคุณimplement) ธง
--//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
- ตัวอย่างแบบต้นทางถึงปลายทางทั้งชุด