กฎจะกำหนดชุดการดำเนินการที่ Bazel ดำเนินการกับข้อมูลอินพุตเพื่อสร้างชุดเอาต์พุต ซึ่งจะอ้างอิงในผู้ให้บริการที่แสดงผลโดยฟังก์ชันการใช้งานของกฎ เช่น C++ กฎไบนารีอาจมีลักษณะดังนี้
- นำชุดไฟล์ต้นทาง
.cpp
(อินพุต) - เรียกใช้
g++
ในไฟล์ต้นฉบับ (การดำเนินการ) - แสดงผลผู้ให้บริการ
DefaultInfo
ที่มีเอาต์พุตที่เรียกใช้งานได้และไฟล์อื่นๆ เพื่อทำให้พร้อมใช้งานขณะรันไทม์ - แสดงผลผู้ให้บริการ
CcInfo
พร้อมข้อมูลเฉพาะ C++ ที่รวบรวมจาก เป้าหมายและการอ้างอิง
จากมุมมองของ Bazel นั้น g++
และไลบรารี C++ มาตรฐานยังเป็นอินพุตด้วย
ลงในกฎนี้ ในฐานะผู้เขียนกฎ คุณต้องพิจารณาไม่เพียงอินพุตที่ผู้ใช้ระบุให้กับกฎเท่านั้น แต่ยังต้องพิจารณาเครื่องมือและไลบรารีทั้งหมดที่จําเป็นต่อการดำเนินการด้วย
ก่อนที่จะสร้างหรือแก้ไขกฎใดๆ โปรดทำความคุ้นเคยกับ สร้างเฟส คุณควรทำความเข้าใจ 3 ระยะของการสร้าง (การโหลด การวิเคราะห์ และการดำเนินการ) นอกจากนี้ คุณยังควรศึกษาเกี่ยวกับมาโครเพื่อทําความเข้าใจความแตกต่างระหว่างกฎกับมาโคร หากต้องการเริ่มต้นใช้งาน ให้อ่านบทแนะนำเกี่ยวกับกฎก่อน จากนั้นใช้หน้านี้เป็นข้อมูลอ้างอิง
Bazel มีกฎบางอย่างในตัว กฎเนทีฟเหล่านี้ เช่น cc_library
และ java_binary
จะให้การสนับสนุนหลักบางอย่างสำหรับบางภาษา
การกำหนดกฎเองจะช่วยให้คุณเพิ่มการรองรับภาษาและเครื่องมือที่คล้ายกันได้
ที่ Bazel ไม่สนับสนุนตั้งแต่แรก
Bazel มีรูปแบบการขยายสำหรับเขียนกฎโดยใช้ภาษา Starlark กฎเหล่านี้เขียนขึ้นในไฟล์ .bzl
ซึ่ง
โหลดได้โดยตรงจาก BUILD
ไฟล์
เมื่อกําหนดกฎของคุณเอง คุณสามารถเลือกแอตทริบิวต์ที่รองรับและวิธีสร้างเอาต์พุต
ฟังก์ชัน implementation
ของกฎจะกำหนดลักษณะการทำงานที่แน่นอนของกฎนั้นๆ ในระหว่างระยะการวิเคราะห์ ฟังก์ชันนี้ไม่ได้เรียกใช้
คำสั่งภายนอก แต่ระบบจะลงทะเบียนการดำเนินการที่จะนำมาใช้ในภายหลังระหว่างระยะการดําเนินการเพื่อสร้างเอาต์พุตของกฎ หากจําเป็น
การสร้างกฎ
ในไฟล์ .bzl
ให้ใช้ฟังก์ชัน rule เพื่อกําหนดกฎใหม่ และจัดเก็บผลลัพธ์ไว้ในตัวแปรส่วนกลาง การเรียกใช้ rule
จะระบุแอตทริบิวต์และฟังก์ชันการใช้งาน
example_library = rule(
implementation = _example_library_impl,
attrs = {
"deps": attr.label_list(),
...
},
)
ซึ่งจะกําหนดประเภทกฎชื่อ example_library
การเรียก rule
จะต้องระบุด้วยหากกฎสร้าง
เอาต์พุตexecutable (ที่มี executable=True
) หรือเฉพาะ
ไฟล์ปฏิบัติการทดสอบ (ที่มี test=True
) หากเป็นกรณีหลัง กฎจะเป็นกฎการทดสอบ
และชื่อของกฎต้องลงท้ายด้วย _test
การสร้างอินสแตนซ์เป้าหมาย
คุณสามารถโหลดและเรียกใช้กฎในไฟล์ BUILD
ได้โดยทำดังนี้
load('//some/pkg:rules.bzl', 'example_library')
example_library(
name = "example_target",
deps = [":another_target"],
...
)
การเรียกไปยังกฎการสร้างแต่ละครั้งจะไม่แสดงค่า แต่มีผลข้างเคียงจากการกำหนด เป้าหมาย วิธีนี้เรียกว่าการตรวจสอบกฎ ซึ่งจะระบุชื่อเป้าหมายใหม่และค่าสําหรับแอตทริบิวต์ของเป้าหมาย
นอกจากนี้ยังสามารถเรียกใช้กฎจากฟังก์ชัน Starlark และโหลดใน .bzl
ไฟล์
ฟังก์ชัน Starlark ที่เรียกกฎเรียกว่ามาโคร Starlark
ท้ายที่สุดแล้วมาโคร Starlark จะต้องถูกเรียกจากไฟล์ BUILD
และจะทำได้เพียง
ในระหว่างระยะการโหลด เมื่อ BUILD
จะได้รับการประเมินเพื่อสร้างอินสแตนซ์เป้าหมาย
Attributes
แอตทริบิวต์คืออาร์กิวเมนต์ของกฎ แอตทริบิวต์สามารถระบุค่าเฉพาะให้กับ การใช้งานของเป้าหมาย หรืออาจอ้างอิงถึง โดยสร้างกราฟของทรัพยากร Dependency ได้
แอตทริบิวต์เฉพาะกฎ เช่น srcs
หรือ deps
จะกำหนดโดยการส่งผ่านแมป
จากชื่อแอตทริบิวต์เป็นสคีมา (สร้างโดยใช้ attr
) กับพารามิเตอร์ attrs
ของ rule
แอตทริบิวต์ทั่วไป เช่น
เพิ่ม name
และ visibility
ลงในกฎทั้งหมดโดยปริยาย ระบบจะเพิ่มแอตทริบิวต์เพิ่มเติมลงในกฎที่เรียกใช้ได้และกฎทดสอบโดยเฉพาะโดยปริยาย แอตทริบิวต์ที่
ถูกเพิ่มลงในกฎโดยปริยาย ไม่สามารถรวมอยู่ในพจนานุกรมที่ส่งไปยัง
attrs
แอตทริบิวต์การขึ้นต่อกัน
กฎที่ประมวลผลซอร์สโค้ดมักจะกำหนดแอตทริบิวต์ต่อไปนี้เพื่อจัดการกับ ทรัพยากร Dependency ประเภทต่างๆ มีดังนี้
srcs
ระบุไฟล์ต้นทางที่ประมวลผลโดยการดำเนินการของเป้าหมาย บ่อยครั้งที่ สคีมาแอตทริบิวต์ระบุนามสกุลไฟล์ที่ควรใช้สำหรับการจัดเรียง ของไฟล์ต้นฉบับที่กฎจะประมวลผล โดยทั่วไป กฎสำหรับภาษาที่มีไฟล์ส่วนหัวจะระบุแอตทริบิวต์hdrs
แยกต่างหากสำหรับส่วนหัวที่ประมวลผลโดยเป้าหมายและผู้ใช้งานdeps
ระบุทรัพยากร Dependency ของโค้ดสําหรับเป้าหมาย สคีมาของแอตทริบิวต์ควร ระบุผู้ให้บริการที่พึ่งพาได้ (สำหรับ เช่นcc_library
จะระบุCcInfo
)data
ระบุไฟล์ที่จะพร้อมใช้งานเมื่อรันไทม์สำหรับไฟล์ที่เรียกใช้งานได้ซึ่งขึ้นอยู่กับเป้าหมาย ซึ่งช่วยให้สามารถกำหนดให้ไฟล์ที่กำหนดเอง ที่ระบุไว้
example_library = rule(
implementation = _example_library_impl,
attrs = {
"srcs": attr.label_list(allow_files = [".example"]),
"hdrs": attr.label_list(allow_files = [".header"]),
"deps": attr.label_list(providers = [ExampleInfo]),
"data": attr.label_list(allow_files = True),
...
},
)
ต่อไปนี้คือตัวอย่างของแอตทริบิวต์การขึ้นต่อกัน แอตทริบิวต์ใดๆ ที่ระบุ
ป้ายกำกับอินพุต (ซึ่งกำหนดด้วย
attr.label_list
attr.label
หรือ
attr.label_keyed_string_dict
)
ระบุทรัพยากร Dependency บางประเภท
ระหว่างเป้าหมายและเป้าหมายที่มีป้ายกำกับ (หรือ
Label
ออบเจ็กต์) จะแสดงอยู่ในแอตทริบิวต์นั้นเมื่อเป้าหมาย
มีการกำหนดไว้ ที่เก็บและอาจกำหนดเส้นทางสำหรับป้ายกำกับเหล่านี้ได้รับการแก้ไขแล้ว
ที่เกี่ยวข้องกับเป้าหมายที่กำหนด
example_library(
name = "my_target",
deps = [":other_target"],
)
example_library(
name = "other_target",
...
)
ในตัวอย่างนี้ other_target
เป็นสิ่งที่ต้องพึ่งพา my_target
ดังนั้นระบบจะวิเคราะห์ other_target
ก่อน จะเป็นข้อผิดพลาดหากมีวงจรในกราฟความเกี่ยวข้องของเป้าหมาย
แอตทริบิวต์ส่วนตัวและทรัพยากร Dependency โดยนัย
แอตทริบิวต์การขึ้นต่อกันซึ่งมีค่าเริ่มต้นจะสร้างการขึ้นต่อกันโดยนัย ทั้งนี้
เป็นนัยเพราะเป็นส่วนหนึ่งของกราฟเป้าหมายที่ผู้ใช้ไม่ได้
ระบุในไฟล์ BUILD
Dependency ที่ไม่ชัดจะใช้ได้กับการเขียนโค้ดความสัมพันธ์ระหว่างกฎกับเครื่องมือ (Dependency ของเวลาสร้าง เช่น คอมไพเลอร์) ไว้ในโค้ดอย่างถาวร เนื่องจากผู้ใช้มักไม่สนใจที่จะระบุเครื่องมือที่กฎใช้ ในฟังก์ชันการใช้งานของกฎ ระบบจะถือว่าค่านี้เหมือนกับการอ้างอิงอื่นๆ
หากคุณต้องการให้การขึ้นต่อกันโดยปริยายโดยไม่อนุญาตให้ผู้ใช้
ลบล้างค่านั้น คุณก็ทำให้แอตทริบิวต์เป็นส่วนตัวได้โดยตั้งชื่อ
ที่ขึ้นต้นด้วยขีดล่าง (_
) แอตทริบิวต์ส่วนตัวต้องมีค่าเริ่มต้น
โดยทั่วไปแล้ว คุณควรใช้แอตทริบิวต์ส่วนตัวเพียงโดยนัยเท่านั้น
ทรัพยากร Dependency
example_library = rule(
implementation = _example_library_impl,
attrs = {
...
"_compiler": attr.label(
default = Label("//tools:example_compiler"),
allow_single_file = True,
executable = True,
cfg = "exec",
),
},
)
ในตัวอย่างนี้ เป้าหมายทุกรายการของประเภท example_library
มีการพึ่งพาคอมไพเลอร์ //tools:example_compiler
โดยนัย วิธีนี้ช่วยให้
ฟังก์ชันการใช้งานของ example_library
เพื่อสร้างการดำเนินการที่เรียกใช้ฟังก์ชัน
คอมไพเลอร์ แม้ผู้ใช้จะไม่ได้ส่งผ่านป้ายกำกับเป็นอินพุตก็ตาม เนื่องจาก _compiler
เป็นแอตทริบิวต์ส่วนตัว ctx.attr._compiler
จึงจะชี้ไปยัง //tools:example_compiler
เสมอในเป้าหมายทั้งหมดของกฎประเภทนี้ หรือจะตั้งชื่อแอตทริบิวต์เป็น compiler
โดยไม่ต้องใส่ขีดล่างและเก็บค่าเริ่มต้นไว้ก็ได้ ซึ่งช่วยให้ผู้ใช้ใช้คอมไพเลอร์อื่นแทนได้หากจำเป็น แต่ไม่ต้องคำนึงถึงป้ายกำกับของคอมไพเลอร์
โดยทั่วไปแล้ว Dependency ที่ไม่ชัดจะใช้กับเครื่องมือที่อยู่ในที่เก็บข้อมูลเดียวกับการติดตั้งใช้งานกฎ หากเครื่องมือมาจากแพลตฟอร์มการเรียกใช้หรือที่เก็บข้อมูลอื่นแทน กฎควรรับเครื่องมือนั้นจากชุดเครื่องมือ
แอตทริบิวต์เอาต์พุต
แอตทริบิวต์เอาต์พุต เช่น attr.output
และ
attr.output_list
ให้ประกาศไฟล์เอาต์พุตที่
สร้างได้ตามเป้าหมาย แอตทริบิวต์เหล่านี้แตกต่างจากแอตทริบิวต์ของ Dependency ตรง 2 ประการดังนี้
- พวกเขากำหนดเป้าหมายไฟล์เอาต์พุตแทนที่จะอ้างอิงเป้าหมายที่กำหนดไว้ ในที่อื่นๆ
- เป้าหมายไฟล์เอาต์พุตจะขึ้นอยู่กับเป้าหมายกฎที่สร้างขึ้น ไม่ใช่ในทางกลับกัน
โดยปกติแล้ว แอตทริบิวต์เอาต์พุตจะใช้ก็ต่อเมื่อกฎต้องสร้างเอาต์พุตที่มีชื่อที่ผู้ใช้กําหนด ซึ่งไม่สามารถอิงตามชื่อเป้าหมายได้ หากกฎมีแอตทริบิวต์เอาต์พุต 1 รายการ โดยทั่วไปชื่อของกฎจะเป็น out
หรือ outs
แอตทริบิวต์เอาต์พุตคือวิธีที่แนะนำในการสร้างเอาต์พุตที่ประกาศไว้ล่วงหน้า ขึ้นอยู่กับหรือ ที่ขอในบรรทัดคำสั่ง
ฟังก์ชันการใช้งาน
กฎทุกข้อต้องมีฟังก์ชัน implementation
ฟังก์ชันเหล่านี้จะทํางานในระยะการวิเคราะห์อย่างเคร่งครัด และเปลี่ยนรูปแบบกราฟของเป้าหมายที่สร้างขึ้นในระยะการโหลดเป็นกราฟของการดําเนินการที่จะดําเนินการในระยะการดําเนินการ ด้วยเหตุนี้
ฟังก์ชันการใช้งานจะไม่สามารถอ่านหรือเขียนไฟล์ได้จริง
ฟังก์ชันการใช้กฎมักจะเป็นแบบส่วนตัว (ตั้งชื่อโดยขึ้นต้นด้วย
ขีดล่าง) ตามธรรมเนียมแล้ว ไฟล์เหล่านี้จะมีชื่อเหมือนกับกฎ แต่จะมี_impl
ต่อท้าย
ฟังก์ชันการติดตั้งใช้งานใช้พารามิเตอร์เพียง 1 ตัว ได้แก่
บริบทของกฎ ซึ่งโดยทั่วไปจะใช้ชื่อว่า ctx
โดยแสดงรายการ
providers
เป้าหมาย
ระบบจะแสดงข้อมูลพึ่งพาเป็นออบเจ็กต์ Target
ในเวลาที่วิเคราะห์ ออบเจ็กต์เหล่านี้มี providers ที่สร้างขึ้นเมื่อมีการเรียกใช้ฟังก์ชันการติดตั้งใช้งานของเป้าหมาย
ctx.attr
มีช่องที่สอดคล้องกับชื่อแอตทริบิวต์การพึ่งพาแต่ละรายการ ซึ่งมีออบเจ็กต์ Target
ที่แสดงถึงความสัมพันธ์โดยตรงแต่ละรายการผ่านแอตทริบิวต์นั้น สำหรับแอตทริบิวต์ label_list
นี่คือรายการของ
Targets
สำหรับแอตทริบิวต์ label
จะเป็น Target
หรือ None
รายการเดียว
ฟังก์ชันการใช้งานของเป้าหมายจะแสดงผลรายการออบเจ็กต์ผู้ให้บริการ ดังนี้
return [ExampleInfo(headers = depset(...))]
คุณสามารถเข้าถึงค่าเหล่านี้ได้โดยใช้สัญลักษณ์ดัชนี ([]
) โดยมีประเภทผู้ให้บริการเป็น
คีย์ ซึ่งอาจเป็นผู้ให้บริการที่กำหนดเองที่กำหนดไว้ใน Starlark หรือ
ผู้ให้บริการสำหรับกฎแบบเนทีฟ พร้อมใช้งานเป็น Starlark
ตัวแปรร่วม
เช่น หากกฎรับไฟล์ส่วนหัวผ่านแอตทริบิวต์ hdrs
และระบุ
ต่อการรวบรวมเป้าหมายและผู้บริโภคเป้าหมาย
รวบรวมข้อมูลได้ดังนี้
def _example_library_impl(ctx):
...
transitive_headers = [hdr[ExampleInfo].headers for hdr in ctx.attr.hdrs]
สําหรับรูปแบบเดิมที่แสดงผล struct
จากฟังก์ชันการติดตั้งใช้งานของเป้าหมายแทนรายการออบเจ็กต์ผู้ให้บริการ ให้ทําดังนี้
return struct(example_info = struct(headers = depset(...)))
คุณสามารถเรียกข้อมูลผู้ให้บริการได้จากช่องที่เกี่ยวข้องของออบเจ็กต์ Target
ดังนี้
transitive_headers = [hdr.example_info.headers for hdr in ctx.attr.hdrs]
ไม่สนับสนุนสไตล์นี้อย่างยิ่ง และควรปฏิบัติตามกฎ อพยพออกจากโดเมนนั้น
ไฟล์
ไฟล์จะแสดงด้วยออบเจ็กต์ File
เนื่องจาก Bazel ไม่
ดำเนินการ I/O ไฟล์ในระหว่างขั้นตอนการวิเคราะห์ ออบเจ็กต์เหล่านี้จะไม่สามารถนำมาใช้เพื่อ
อ่านหรือเขียนเนื้อหาไฟล์โดยตรง แต่จะส่งไปให้กับการปล่อยการกระทำ
(ดู ctx.actions
) เพื่อสร้างส่วน
กราฟการดำเนินการ
File
อาจเป็นไฟล์ต้นฉบับหรือไฟล์ที่สร้างขึ้นก็ได้ ไฟล์ที่สร้างขึ้นแต่ละรายการ
ต้องเป็นเอาต์พุตของการดำเนินการ 1 รายการเท่านั้น ไฟล์ต้นฉบับต้องไม่ใช่เอาต์พุตของการดำเนินการใดๆ
สําหรับแอตทริบิวต์การพึ่งพาแต่ละรายการ ช่องที่เกี่ยวข้องของ ctx.files
จะมีรายการเอาต์พุตเริ่มต้นของการพึ่งพาทั้งหมดผ่านแอตทริบิวต์นั้น
def _example_library_impl(ctx):
...
headers = depset(ctx.files.hdrs, transitive=transitive_headers)
srcs = ctx.files.srcs
...
ctx.file
มี File
หรือ None
รายการเดียวสำหรับแอตทริบิวต์ Dependency ที่กำหนดข้อกำหนดเป็น allow_single_file=True
ctx.executable
ทำงานเหมือนกับ ctx.file
แต่มีเพียง
มีฟิลด์สำหรับแอตทริบิวต์ทรัพยากร Dependency ที่มีการตั้งค่าข้อกำหนดของ executable=True
การประกาศเอาต์พุต
ในช่วงการวิเคราะห์ ฟังก์ชันการใช้งานของกฎจะสร้างเอาต์พุตได้
เนื่องจากต้องทราบป้ายกำกับทั้งหมดในระยะการโหลด เอาต์พุตเพิ่มเติมเหล่านี้จึงไม่มีป้ายกำกับ คุณสร้างออบเจ็กต์ File
รายการสำหรับเอาต์พุตได้โดยใช้
ctx.actions.declare_file
และ
ctx.actions.declare_directory
โดยทั่วไปแล้ว ชื่อเอาต์พุตจะอิงตามชื่อของเป้าหมาย ctx.label.name
def _example_library_impl(ctx):
...
output_file = ctx.actions.declare_file(ctx.label.name + ".output")
...
สําหรับเอาต์พุตที่ประกาศไว้ล่วงหน้า เช่น เอาต์พุตที่สร้างสําหรับแอตทริบิวต์เอาต์พุต ระบบจะดึงข้อมูลออบเจ็กต์ File
จากช่องที่เกี่ยวข้องของ ctx.outputs
แทน
การทำงาน
การดำเนินการจะอธิบายวิธีสร้างชุดเอาต์พุตจากชุดอินพุต เช่น "เรียกใช้ gcc ใน hello.c และรับ hello.o" เมื่อมีการสร้างการกระทำขึ้น Bazel จะไม่เรียกใช้คำสั่งโดยทันที โดยจะบันทึกไว้ในกราฟของความสัมพันธ์ เนื่องจากการดำเนินการหนึ่งๆ อาจขึ้นอยู่กับเอาต์พุตของการดำเนินการอื่น ตัวอย่างเช่น ใน C ต้องเรียก linker หลังคอมไพเลอร์
ฟังก์ชันวัตถุประสงค์ทั่วไปที่สร้างการกระทำจะกำหนดไว้ใน
ctx.actions
ctx.actions.run
เพื่อเรียกใช้ไฟล์ปฏิบัติการctx.actions.run_shell
เพื่อเรียกใช้เชลล์ คำสั่งctx.actions.write
เพื่อเขียนสตริงลงในไฟล์ctx.actions.expand_template
ถึง สร้างไฟล์จากเทมเพลต
ctx.actions.args
สามารถใช้เพื่อเพิ่มประสิทธิภาพ
รวบรวมอาร์กิวเมนต์สำหรับการดำเนินการ เพื่อหลีกเลี่ยงการหน่วงเวลาเลิกปรับจนถึง
เวลาดำเนินการ:
def _example_library_impl(ctx):
...
transitive_headers = [dep[ExampleInfo].headers for dep in ctx.attr.deps]
headers = depset(ctx.files.hdrs, transitive=transitive_headers)
srcs = ctx.files.srcs
inputs = depset(srcs, transitive=[headers])
output_file = ctx.actions.declare_file(ctx.label.name + ".output")
args = ctx.actions.args()
args.add_joined("-h", headers, join_with=",")
args.add_joined("-s", srcs, join_with=",")
args.add("-o", output_file)
ctx.actions.run(
mnemonic = "ExampleCompile",
executable = ctx.executable._compiler,
arguments = [args],
inputs = inputs,
outputs = [output_file],
)
...
การดำเนินการจะนํารายการหรือชุดไฟล์อินพุตมาสร้างรายการไฟล์เอาต์พุต (ที่ไม่ใช่ไฟล์ว่าง) คุณต้องทราบชุดไฟล์อินพุตและเอาต์พุตในระยะการวิเคราะห์ อาจขึ้นอยู่กับค่าของ แอตทริบิวต์ ซึ่งรวมถึงผู้ให้บริการจากทรัพยากร Dependency ได้ แต่ต้องไม่อ้างอิง ของการดำเนินการ ตัวอย่างเช่น หากการทำงานของคุณเรียกใช้คำสั่งคลายการบีบอัดไฟล์ ต้องระบุไฟล์ที่คุณคาดว่าจะพอง (ก่อนเรียกใช้การคลายการบีบอัด) การดำเนินการที่สร้างไฟล์ภายในจํานวนไม่คงที่สามารถรวมไฟล์เหล่านั้นไว้ในไฟล์เดียวได้ (เช่น zip, tar หรือรูปแบบไฟล์เก็บถาวรอื่นๆ)
การดำเนินการต้องป้อนข้อมูลทั้งหมด อนุญาตให้ป้อนข้อมูลรายการที่ไม่ได้ใช้ แต่จะไม่มีประสิทธิภาพ
การดำเนินการต้องสร้างเอาต์พุตทั้งหมด พวกเขาอาจเขียนไฟล์อื่นๆ แต่ ข้อมูลใดก็ตามที่ไม่ได้อยู่ในเอาต์พุตจะไม่พร้อมใช้งานสำหรับผู้บริโภค เอาต์พุตที่ประกาศทั้งหมดต้องเขียนโดยการดำเนินการบางอย่าง
การดำเนินการจะคล้ายกับฟังก์ชันบริสุทธิ์ กล่าวคือ ควรใช้เฉพาะอินพุตที่ระบุไว้ และหลีกเลี่ยงการเข้าถึงข้อมูลคอมพิวเตอร์ ชื่อผู้ใช้ นาฬิกา เครือข่าย หรืออุปกรณ์ I/O (ยกเว้นการอ่านอินพุตและการเขียนเอาต์พุต) นี่คือ สำคัญเพราะระบบจะแคชเอาต์พุตและนำมาใช้ซ้ำ
Bazel จะแก้ไขการพึ่งพา ซึ่งจะเป็นผู้ตัดสินใจว่าจะใช้การดำเนินการใด โดยเป็นข้อผิดพลาดหากมีวงจรในกราฟทรัพยากร Dependency กำลังสร้าง การกระทำไม่ได้รับประกันว่าจะได้ดำเนินการ ซึ่งขึ้นอยู่กับว่า ต้องมีเอาต์พุตของบิลด์สำหรับบิลด์
ผู้ให้บริการ
ผู้ให้บริการคือข้อมูลบางส่วนที่กฎแสดงต่อกฎอื่นๆ ที่ใช้ข้อมูลนั้น ข้อมูลนี้อาจรวมถึงไฟล์เอาต์พุต ไลบรารี พารามิเตอร์ที่จะส่งในบรรทัดคำสั่งของเครื่องมือ หรือสิ่งอื่นๆ ที่ผู้บริโภคเป้าหมายควรทราบ
เนื่องจากฟังก์ชันการใช้งานของกฎจะอ่านได้เฉพาะผู้ให้บริการจากข้อกําหนดเบื้องต้นโดยตรงของเป้าหมายที่สร้างขึ้น กฎจึงต้องส่งต่อข้อมูลจากข้อกําหนดเบื้องต้นของเป้าหมายที่ผู้ใช้ปลายทางของเป้าหมายจําเป็นต้องทราบ โดยปกติแล้วจะเป็นการเก็บรวบรวมข้อมูลดังกล่าวไว้ใน depset
ผู้ให้บริการของเป้าหมายจะระบุด้วยรายการออบเจ็กต์ Provider
ที่แสดงผลโดยฟังก์ชันการติดตั้งใช้งาน
ฟังก์ชันการใช้งานแบบเก่ายังสามารถเขียนในรูปแบบเดิมที่
แสดงผลฟังก์ชันการใช้งาน struct
แทนที่จะเป็นรายการ
ออบเจ็กต์ผู้ให้บริการ ไม่สนับสนุนสไตล์นี้อย่างยิ่ง และควรปฏิบัติตามกฎ
อพยพออกจากโดเมนนั้น
เอาต์พุตเริ่มต้น
เอาต์พุตเริ่มต้นของเป้าหมายคือเอาต์พุตที่มีการขอโดยค่าเริ่มต้นเมื่อ
มีการขอเป้าหมายสำหรับบิลด์ในบรรทัดคำสั่ง ตัวอย่างเช่น java_library
เป้าหมาย //pkg:foo
มี foo.jar
เป็นเอาต์พุตเริ่มต้น ดังนั้น foo.jar
จะถูกสร้างขึ้นโดยคำสั่ง bazel build //pkg:foo
เอาต์พุตเริ่มต้นจะระบุโดยพารามิเตอร์ files
ของ
DefaultInfo
:
def _example_library_impl(ctx):
...
return [
DefaultInfo(files = depset([output_file]), ...),
...
]
หากการใช้งานกฎไม่ได้แสดงผล DefaultInfo
หรือไม่ได้ระบุพารามิเตอร์ files
DefaultInfo.files
จะแสดงผลเป็นเอาต์พุตที่ประกาศไว้ล่วงหน้าทั้งหมดโดยค่าเริ่มต้น (โดยทั่วไปคือเอาต์พุตที่สร้างโดยแอตทริบิวต์เอาต์พุต)
กฎที่ดําเนินการควรให้เอาต์พุตเริ่มต้น แม้ว่าจะไม่คาดว่าจะมีการใช้เอาต์พุตเหล่านั้นโดยตรงก็ตาม การดำเนินการที่ไม่ได้อยู่ในกราฟของ เอาต์พุตที่ขอถูกตัดทอน หากเอาต์พุตใช้โดยผู้บริโภคเป้าหมายเท่านั้น การดำเนินการเหล่านั้นจะไม่เกิดขึ้นเมื่อมีการสร้างเป้าหมายแยกออกมา ช่วงเวลานี้ ทำให้การแก้ไขข้อบกพร่องยากขึ้น เพราะการสร้างเป้าหมายที่ล้มเหลวใหม่นั้นไม่ได้ สร้างขั้นตอนที่ไม่สำเร็จอีกครั้ง
Runfiles
ไฟล์รันไทม์คือชุดไฟล์ที่เป้าหมายใช้ขณะรันไทม์ (ไม่ใช่ขณะสร้าง) ในระหว่างระยะการดําเนินการ Bazel จะสร้างต้นไม้ไดเรกทอรีที่มีลิงก์สัญลักษณ์ซึ่งชี้ไปยังไฟล์รันไทม์ ขั้นตอนนี้ช่วยให้ สภาพแวดล้อมของไบนารีเพื่อให้เข้าถึงไฟล์เรียกใช้ระหว่างรันไทม์ได้
เพิ่มไฟล์เรียกใช้ด้วยตนเองได้ในระหว่างการสร้างกฎ
สามารถสร้างออบเจ็กต์ runfiles
โดยใช้เมธอด runfiles
ในบริบทกฎ ctx.runfiles
และส่งไปยังพารามิเตอร์ runfiles
ใน DefaultInfo
เอาต์พุตไฟล์ปฏิบัติการของ
กฎที่สั่งการได้จะเพิ่มเข้าไปในไฟล์รันไฟล์โดยปริยาย
กฎบางอย่างจะระบุแอตทริบิวต์ ซึ่งโดยทั่วไปจะใช้ชื่อว่า
data
ที่เพิ่มเอาต์พุตไปยัง
เป้าหมาย runfiles. นอกจากนี้ คุณควรผสานไฟล์รันไทม์จาก data
รวมถึงจากแอตทริบิวต์ใดๆ ที่อาจให้โค้ดสําหรับการเรียกใช้ขั้นสุดท้าย ซึ่งโดยทั่วไปแล้วคือ srcs
(ซึ่งอาจมีเป้าหมาย filegroup
ที่มี data
ที่เกี่ยวข้อง) และ deps
def _example_library_impl(ctx):
...
runfiles = ctx.runfiles(files = ctx.files.data)
transitive_runfiles = []
for runfiles_attr in (
ctx.attr.srcs,
ctx.attr.hdrs,
ctx.attr.deps,
ctx.attr.data,
):
for target in runfiles_attr:
transitive_runfiles.append(target[DefaultInfo].default_runfiles)
runfiles = runfiles.merge_all(transitive_runfiles)
return [
DefaultInfo(..., runfiles = runfiles),
...
]
ผู้ให้บริการที่กําหนดเอง
คุณสามารถกำหนดผู้ให้บริการโดยใช้ provider
สำหรับสื่อสารข้อมูลเฉพาะกฎ:
ExampleInfo = provider(
"Info needed to compile/link Example code.",
fields={
"headers": "depset of header Files from transitive dependencies.",
"files_to_link": "depset of Files from compilation.",
})
จากนั้นฟังก์ชันการใช้งานกฎจะสร้างและแสดงผลอินสแตนซ์ของผู้ให้บริการได้ดังนี้
def _example_library_impl(ctx):
...
return [
...
ExampleInfo(
headers = headers,
files_to_link = depset(
[output_file],
transitive = [
dep[ExampleInfo].files_to_link for dep in ctx.attr.deps
],
),
)
]
เริ่มต้นใช้งานผู้ให้บริการที่กำหนดเอง
คุณสามารถป้องกันการสร้างอินสแตนซ์ของผู้ให้บริการด้วยตรรกะการประมวลผลก่อนการประมวลผลและการตรวจสอบที่กำหนดเอง ซึ่งสามารถใช้เพื่อให้อินสแตนซ์ผู้ให้บริการทั้งหมดเป็นไปตามอินตัวแปรที่ไม่เปลี่ยนแปลง หรือเพื่อให้ผู้ใช้ได้รับ API ที่สะอาดขึ้นสำหรับการรับอินสแตนซ์
ซึ่งทำได้โดยการส่งผ่าน init
Callback ไปยัง
provider
หากมีการเรียกกลับนี้
ประเภทผลลัพธ์ของ provider()
การเปลี่ยนแปลงเป็น 2 ค่าเป็น 2 ค่า ได้แก่ ค่าผู้ให้บริการ
ซึ่งเป็นค่าการแสดงผลปกติเมื่อไม่มีการใช้ init
และ "ข้อมูลดิบ
ของตัวสร้าง"
ในกรณีนี้ เมื่อเรียกใช้สัญลักษณ์ผู้ให้บริการ แทนที่จะแสดงอินสแตนซ์ใหม่โดยตรง ระบบจะส่งต่ออาร์กิวเมนต์ไปยัง init
callback ค่าที่แสดงผลของ callback ต้องเป็น dict ที่แมปชื่อฟิลด์ (สตริง) กับค่า ซึ่งจะใช้เพื่อเริ่มต้นฟิลด์ของอินสแตนซ์ใหม่ โปรดทราบว่า
Callback อาจมีลายเซ็นและหากอาร์กิวเมนต์ไม่ตรงกับลายเซ็น
ระบบจะรายงานข้อผิดพลาดเสมือนว่ามีการเรียกใช้ Callback โดยตรง
ในทางตรงกันข้าม ตัวสร้างดิบจะข้ามการเรียกกลับ init
ตัวอย่างต่อไปนี้ใช้ init
เพื่อประมวลผลก่อนและตรวจสอบอาร์กิวเมนต์
# //pkg:exampleinfo.bzl
_core_headers = [...] # private constant representing standard library files
# It's possible to define an init accepting positional arguments, but
# keyword-only arguments are preferred.
def _exampleinfo_init(*, files_to_link, headers = None, allow_empty_files_to_link = False):
if not files_to_link and not allow_empty_files_to_link:
fail("files_to_link may not be empty")
all_headers = depset(_core_headers, transitive = headers)
return {'files_to_link': files_to_link, 'headers': all_headers}
ExampleInfo, _new_exampleinfo = provider(
...
init = _exampleinfo_init)
export ExampleInfo
จากนั้นการติดตั้งใช้งานกฎอาจสร้างอินสแตนซ์ของผู้ให้บริการดังนี้
ExampleInfo(
files_to_link=my_files_to_link, # may not be empty
headers = my_headers, # will automatically include the core headers
)
ตัวสร้างดิบสามารถนำมาใช้กำหนดฟังก์ชันของโรงงานสาธารณะอื่นๆ ได้
ที่ไม่ผ่านตรรกะ init
ตัวอย่างเช่น ใน exampleinfo.bzl เรา
อาจมีความหมายดังนี้
def make_barebones_exampleinfo(headers):
"""Returns an ExampleInfo with no files_to_link and only the specified headers."""
return _new_exampleinfo(files_to_link = depset(), headers = all_headers)
โดยทั่วไปแล้ว ตัวสร้างดิบจะถูกผูกไว้กับตัวแปรที่มีชื่อเริ่มต้นด้วย
ขีดล่าง (_new_exampleinfo
ด้านบน) เพื่อไม่ให้รหัสผู้ใช้โหลดและ
สร้างอินสแตนซ์ผู้ให้บริการที่กำหนดเอง
การใช้งานอีกอย่างหนึ่งสำหรับ init
คือการป้องกันไม่ให้ผู้ใช้โทรหาผู้ให้บริการ
ทั้งหมด และบังคับให้ใช้ฟังก์ชันจากโรงงานแทน
def _exampleinfo_init_banned(*args, **kwargs):
fail("Do not call ExampleInfo(). Use make_exampleinfo() instead.")
ExampleInfo, _new_exampleinfo = provider(
...
init = _exampleinfo_init_banned)
def make_exampleinfo(...):
...
return _new_exampleinfo(...)
กฎที่เรียกใช้ได้และกฎทดสอบ
กฎปฏิบัติการจะกำหนดเป้าหมายที่คำสั่ง bazel run
เรียกใช้ได้
กฎทดสอบเป็นกฎที่เรียกใช้ได้แบบพิเศษซึ่งสามารถเรียกใช้เป้าหมายด้วยคำสั่ง bazel test
กฎที่เรียกใช้ได้และกฎทดสอบสร้างขึ้นโดยการกำหนดอาร์กิวเมนต์ executable
หรือ test
ที่เกี่ยวข้องเป็น True
ในการเรียกใช้ rule
ดังนี้
example_binary = rule(
implementation = _example_binary_impl,
executable = True,
...
)
example_test = rule(
implementation = _example_binary_impl,
test = True,
...
)
กฎการทดสอบต้องมีชื่อที่ลงท้ายด้วย _test
(ชื่อเป้าหมายการทดสอบมักจะลงท้ายด้วย _test
ตามแบบแผน แต่ก็ไม่บังคับ) กฎที่ไม่ใช่การทดสอบต้องไม่มีส่วนต่อท้ายนี้
กฎทั้ง 2 ประเภทจะต้องสร้างไฟล์เอาต์พุตที่สั่งการได้ (ซึ่งอาจ
ได้รับการประกาศล่วงหน้า) ที่จะเรียกใช้โดยคำสั่ง run
หรือ test
จะบอก
Bazel ว่าเอาต์พุตของกฎใดที่จะใช้เป็นไฟล์ปฏิบัติการนี้ แล้วส่งเป็น
อาร์กิวเมนต์ executable
ของ DefaultInfo
ที่แสดงผล
ระบบจะเพิ่ม executable
นั้นลงในเอาต์พุตเริ่มต้นของกฎ (คุณจึงไม่ต้องส่งค่านั้นไปยังทั้ง executable
และ files
) และจะเพิ่มลงใน runfiles โดยปริยายด้วย
def _example_binary_impl(ctx):
executable = ctx.actions.declare_file(ctx.label.name)
...
return [
DefaultInfo(executable = executable, ...),
...
]
การดำเนินการที่สร้างไฟล์นี้ต้องตั้งค่าบิตปฏิบัติการในไฟล์ สำหรับ
ctx.actions.run
หรือ
การดำเนินการ ctx.actions.run_shell
รายการที่ควรทำ
ในเครื่องมือพื้นฐานที่เรียกใช้โดยการดำเนินการ สําหรับการดําเนินการ ctx.actions.write
ให้ส่ง is_executable=True
ตามลักษณะการทำงานเดิม กฎที่ดำเนินการได้จะมี
เอาต์พุตพิเศษที่ประกาศไว้ล่วงหน้าของ ctx.outputs.executable
ไฟล์นี้จะใช้เป็นไฟล์ที่ดำเนินการได้เริ่มต้นหากคุณไม่ได้ระบุไฟล์โดยใช้ DefaultInfo
และจะต้องไม่ใช้ไฟล์นี้ในกรณีอื่น เราไม่แนะนำให้ใช้กลไกเอาต์พุตนี้เนื่องจากไม่รองรับการปรับแต่งชื่อไฟล์ที่เรียกใช้งานได้ ณ เวลาวิเคราะห์
ดูตัวอย่างกฎที่เรียกใช้ได้และกฎทดสอบ
กฎปฏิบัติการและ กฎการทดสอบมี ที่ระบุโดยปริยาย นอกเหนือจากแอตทริบิวต์ที่เพิ่มสำหรับ กฎทั้งหมด ค่าเริ่มต้นของ ไม่สามารถเปลี่ยนแอตทริบิวต์ที่เพิ่มโดยปริยาย แต่ปัญหานี้แก้ไขได้ โดยการรวมกฎส่วนตัวในมาโคร Starlark ซึ่งเปลี่ยน ค่าเริ่มต้น:
def example_test(size="small", **kwargs):
_example_test(size=size, **kwargs)
_example_test = rule(
...
)
ตำแหน่งของไฟล์รันไทม์
เมื่อเป้าหมายที่เรียกใช้ได้ทำงานด้วย bazel run
(หรือ test
) รูทของ
ไดเรกทอรี Runfiles อยู่ติดกับไฟล์ปฏิบัติการ เส้นทางมีความเกี่ยวข้องกันดังนี้
# Given launcher_path and runfile_file:
runfiles_root = launcher_path.path + ".runfiles"
workspace_name = ctx.workspace_name
runfile_path = runfile_file.short_path
execution_root_relative_path = "%s/%s/%s" % (
runfiles_root, workspace_name, runfile_path)
เส้นทางไปยัง File
ภายใต้ไดเรกทอรี Runfiles สอดคล้องกับ
File.short_path
ไบนารีที่เรียกใช้โดย bazel
โดยตรงนั้นอยู่ติดกับรากของ
ไดเรกทอรี runfiles
อย่างไรก็ตาม ไบนารีที่ถูกเรียก from ที่เรียกใช้ไฟล์จะทำไม่ได้
ด้วยสมมติฐานเดียวกัน เพื่อลดปัญหานี้ ไบนารีแต่ละแบบควรให้วิธี
ยอมรับรากของ Runfiles เป็นพารามิเตอร์โดยใช้สภาพแวดล้อมหรือบรรทัดคำสั่ง
อาร์กิวเมนต์/แฟล็ก วิธีนี้จะช่วยให้ไบนารีสามารถส่งผ่านรูทของไฟล์ Canonical ที่ถูกต้องได้
ลงในไบนารีที่การเรียก หากไม่ได้ตั้งค่า ไบนารีจะสามารถเดาได้ว่านี่เป็น
ไบนารีแรกจะเรียกและมองหาไดเรกทอรี Runfiles ที่อยู่ติดกัน
หัวข้อขั้นสูง
การขอไฟล์เอาต์พุต
เป้าหมายเดียวมีไฟล์เอาต์พุตได้หลายไฟล์ เมื่อคำสั่ง bazel build
ให้เรียกใช้ เอาต์พุตบางส่วนของเป้าหมายให้กับคำสั่งนั้น
ได้รับคำขอ Bazel จะสร้างเฉพาะไฟล์ที่ร้องขอเหล่านี้ และไฟล์ที่
อาจเกี่ยวข้องทั้งทางตรงและทางอ้อม (ในแง่ของกราฟการดำเนินการ Bazel จะดำเนินการเฉพาะการดำเนินการที่เข้าถึงได้ในฐานะทรัพยากร Dependency แบบเปลี่ยนผ่านของไฟล์ที่ขอ)
นอกเหนือจากเอาต์พุตเริ่มต้นแล้ว เอาต์พุตที่ประกาศไว้ล่วงหน้าทั้งหมดยังทำสิ่งต่อไปนี้ได้
ในบรรทัดคำสั่งอย่างชัดเจน กฎสามารถระบุเอาต์พุตที่ประกาศไว้ล่วงหน้าผ่านแอตทริบิวต์เอาต์พุต ในกรณีนี้ ผู้ใช้
เลือกป้ายกำกับสำหรับเอาต์พุตอย่างชัดเจนเมื่อสร้างอินสแตนซ์กฎ หากต้องการรับออบเจ็กต์ File
สำหรับแอตทริบิวต์เอาต์พุต ให้ใช้แอตทริบิวต์ที่เกี่ยวข้องของ ctx.outputs
กฎยังกำหนดเอาต์พุตที่ประกาศไว้ล่วงหน้าโดยนัยตามชื่อเป้าหมายได้ด้วย แต่เราไม่แนะนำให้ใช้ฟีเจอร์นี้
นอกจากเอาต์พุตเริ่มต้นแล้ว ยังมีกลุ่มเอาต์พุต ซึ่งเป็นคอลเล็กชันไฟล์เอาต์พุตที่อาจขอพร้อมกันได้ คุณขอรายการเหล่านี้ได้โดยใช้
--output_groups
เช่น หาก //pkg:mytarget
เป้าหมายเป็นกฎประเภทที่มีกลุ่มเอาต์พุต debug_files
ระบบจะสร้างไฟล์เหล่านี้ได้โดยการรัน bazel build //pkg:mytarget
--output_groups=debug_files
เนื่องจากเอาต์พุตที่ไม่ได้ประกาศไว้ล่วงหน้าจะไม่มีป้ายกำกับ คุณจึงขอเอาต์พุตดังกล่าวได้โดยการปรากฏในเอาต์พุตเริ่มต้นหรือกลุ่มเอาต์พุตเท่านั้น
สามารถระบุกลุ่มเอาต์พุตด้วยฟังก์ชัน
OutputGroupInfo
โปรดทราบว่าสิ่งที่ต่างจาก
ผู้ให้บริการที่มีมาในตัว OutputGroupInfo
สามารถรับพารามิเตอร์ที่มีชื่อที่กำหนดเองได้
เพื่อกำหนดกลุ่มเอาต์พุตด้วยชื่อนั้น
def _example_library_impl(ctx):
...
debug_file = ctx.actions.declare_file(name + ".pdb")
...
return [
DefaultInfo(files = depset([output_file]), ...),
OutputGroupInfo(
debug_files = depset([debug_file]),
all_files = depset([output_file, debug_file]),
),
...
]
และต่างจากผู้ให้บริการส่วนใหญ่ตรงที่ OutputGroupInfo
สามารถแสดงผลโดยทั้ง
aspect และเป้าหมายกฎที่จะใช้กับลักษณะนั้น เช่น
ตราบใดที่ไม่ได้กำหนดกลุ่มเอาต์พุตเดียวกัน ในกรณีนี้ ระบบจะผสานผู้ให้บริการที่รวมกัน
โปรดทราบว่าโดยทั่วไปแล้ว OutputGroupInfo
ไม่ควรใช้เพื่อสื่อถึงการจัดเรียงไฟล์ที่เฉพาะเจาะจงจากเป้าหมายไปยังการกระทําของผู้บริโภค ให้กําหนดผู้ให้บริการเฉพาะกฎแทน
การกำหนดค่า
สมมติว่าคุณต้องการสร้างไบนารี C++ สําหรับสถาปัตยกรรมอื่น อาจมีความซับซ้อนและมีหลายขั้นตอน ไฟล์ไบนารีระดับกลางบางไฟล์ เช่น คอมไพเลอร์และเครื่องมือสร้างโค้ด ต้องทำงานบนแพลตฟอร์มการเรียกใช้ (ซึ่งอาจเป็นโฮสต์หรือผู้ดำเนินการระยะไกล) ต้องสร้างไบนารีบางรายการ เช่น เอาต์พุตสุดท้าย สำหรับสถาปัตยกรรมเป้าหมาย
ด้วยเหตุนี้ Bazel จึงเกิดแนวคิดเกี่ยวกับ "การกำหนดค่า" และทรานซิชัน เป้าหมายที่อยู่บนสุด (เป้าหมายที่ขอในบรรทัดคำสั่ง) จะสร้างขึ้นในการกําหนดค่า "target" ส่วนเครื่องมือที่ควรทํางานบนแพลตฟอร์มการดําเนินการจะสร้างขึ้นในการกําหนดค่า "exec" กฎอาจสร้างการดำเนินการที่แตกต่างกันตามการกำหนดค่า เช่น เปลี่ยนสถาปัตยกรรม CPU ที่ส่งไปยังคอมไพเลอร์ ในบางกรณี คุณอาจต้องใช้คลังเดียวกันสำหรับการกําหนดค่าที่แตกต่างกัน หากเกิดเหตุการณ์นี้ขึ้น เราจะวิเคราะห์และสร้างสภาพแวดล้อมดังกล่าว หลายครั้ง
โดยค่าเริ่มต้น Bazel จะสร้างทรัพยากร Dependency ของเป้าหมายในการกำหนดค่าเดียวกันกับ ตัวเป้าหมายเอง กล่าวคือไม่มีการเปลี่ยน เมื่อการอ้างอิงเป็น เครื่องมือที่จำเป็นเพื่อช่วยสร้างเป้าหมาย แอตทริบิวต์ที่เกี่ยวข้อง ระบุการเปลี่ยนไปยังการกำหนดค่า exec ซึ่งจะทำให้เครื่องมือและข้อกําหนดทั้งหมดของเครื่องมือสร้างขึ้นสําหรับแพลตฟอร์มการดําเนินการ
สําหรับแอตทริบิวต์การพึ่งพาแต่ละรายการ คุณสามารถใช้ cfg
เพื่อตัดสินใจว่าควรสร้างการพึ่งพาในการกําหนดค่าเดียวกันหรือเปลี่ยนไปใช้การกําหนดค่า exec
หากแอตทริบิวต์การขึ้นต่อกันมี Flag executable=True
คุณต้องตั้งค่า cfg
อย่างชัดเจน การดำเนินการนี้เพื่อป้องกันการสร้างเครื่องมือสำหรับการกำหนดค่าที่ไม่ถูกต้องโดยไม่ตั้งใจ
ดูตัวอย่าง
โดยทั่วไป ซอร์ส ไลบรารีที่ต้องพึ่งพา และไฟล์ปฏิบัติการซึ่งจำเป็นต้องใช้ที่ ใช้การกำหนดค่าแบบเดียวกันได้
เครื่องมือที่ดำเนินการเป็นส่วนหนึ่งของบิลด์ (เช่น คอมไพเลอร์หรือเครื่องมือสร้างโค้ด) ควรสร้างสำหรับการกำหนดค่า exec ในกรณีนี้ ให้ระบุ cfg="exec"
ในแอตทริบิวต์
มิเช่นนั้น ไฟล์ปฏิบัติการที่ใช้ขณะรันไทม์ (เช่น เป็นส่วนหนึ่งของการทดสอบ) ควร
สร้างขึ้นสำหรับการกำหนดค่าเป้าหมาย ในกรณีนี้ ให้ระบุ cfg="target"
ใน
แอตทริบิวต์
จริงๆ แล้ว cfg="target"
ไม่ได้ทำอะไรเลย แค่ทำให้รู้สึกสะดวกใจเท่านั้น
ช่วยให้ผู้ออกแบบกฎ
แสดงเจตนาของตนอย่างชัดเจน เมื่อ executable=False
ซึ่งหมายความว่า cfg
ไม่บังคับ ให้ตั้งค่านี้เฉพาะในกรณีที่ช่วยให้อ่านได้ง่ายจริงๆ
นอกจากนี้ คุณยังใช้ cfg=my_transition
เพื่อใช้การเปลี่ยนที่กำหนดโดยผู้ใช้ได้ด้วย ซึ่งช่วยให้ผู้เขียนกฎมีความยืดหยุ่นอย่างมากในการเปลี่ยนแปลงการกำหนดค่า แต่ข้อเสียคือทำให้กราฟบิลด์มีขนาดใหญ่ขึ้นและเข้าใจได้ยากขึ้น
หมายเหตุ: ก่อนหน้านี้ Bazel ไม่มีแนวคิดเกี่ยวกับแพลตฟอร์มการดำเนินการ และระบบจะถือว่าการดำเนินการทั้งหมดของการสร้างทำงานบนเครื่องโฮสต์แทน บาเซล เวอร์ชันก่อน 6.0 จะสร้าง "โฮสต์" ที่แตกต่างกัน การกำหนดค่าเพื่อแสดงสิ่งนี้ หากคุณเห็นการอ้างอิงถึง "โฮสต์" ในโค้ดหรือเอกสารประกอบเก่า รายการนี้หมายถึงโฮสต์ดังกล่าว เราขอแนะนำให้ใช้ Bazel 6.0 หรือใหม่กว่าเพื่อหลีกเลี่ยงแนวคิดพิเศษนี้ โอเวอร์เฮด
ส่วนย่อยของการกำหนดค่า
กฎอาจเข้าถึงข้อมูลโค้ดการกําหนดค่า เช่น cpp
, java
และ jvm
อย่างไรก็ตาม คุณต้องประกาศส่วนย่อยที่จำเป็นทั้งหมดใน
เพื่อหลีกเลี่ยงข้อผิดพลาดในการเข้าถึง ให้ทำดังนี้
def _impl(ctx):
# Using ctx.fragments.cpp leads to an error since it was not declared.
x = ctx.fragments.java
...
my_rule = rule(
implementation = _impl,
fragments = ["java"], # Required fragments of the target configuration
host_fragments = ["java"], # Required fragments of the host configuration
...
)
ลิงก์สัญลักษณ์ของ Runfile
โดยปกติเส้นทางแบบสัมพัทธ์ของไฟล์ในแผนผังการเรียกใช้ไฟล์จะเหมือนกับแท็ก
เส้นทางแบบสัมพัทธ์ของไฟล์นั้นในแผนผังแหล่งที่มาหรือแผนผังเอาต์พุตที่สร้างขึ้น หากต้องแตกต่างกันด้วยเหตุผลบางอย่าง คุณสามารถระบุอาร์กิวเมนต์ root_symlinks
หรือ symlinks
root_symlinks
เป็นเส้นทางการแมปพจนานุกรมไปยัง
โดยที่เส้นทางสัมพันธ์กับรูทของไดเรกทอรี Runfiles พจนานุกรม symlinks
จะเหมือนกัน แต่เส้นทางจะมีชื่อของเวิร์กスペースหลักอยู่ข้างหน้าโดยนัย (ไม่ใช่ชื่อของที่เก็บซึ่งมีเป้าหมายปัจจุบัน)
...
runfiles = ctx.runfiles(
root_symlinks = {"some/path/here.foo": ctx.file.some_data_file2}
symlinks = {"some/path/here.bar": ctx.file.some_data_file3}
)
# Creates something like:
# sometarget.runfiles/
# some/
# path/
# here.foo -> some_data_file2
# <workspace_name>/
# some/
# path/
# here.bar -> some_data_file3
หากใช้ symlinks
หรือ root_symlinks
โปรดระมัดระวังอย่าแมปไฟล์ 2 ไฟล์ที่แตกต่างกันไปยังเส้นทางเดียวกันในลําดับชั้น runfiles ซึ่งจะทำให้บิลด์ล้มเหลว
มีข้อผิดพลาดที่อธิบายความขัดแย้ง หากต้องการแก้ไข คุณจะต้องแก้ไข
ctx.runfiles
อาร์กิวเมนต์ที่จะนำการชนออก ระบบจะตรวจสอบเป้าหมายที่ใช้กฎของคุณ รวมถึงเป้าหมายทุกประเภทที่ขึ้นอยู่กับเป้าหมายเหล่านั้น ซึ่งจะเสี่ยงอย่างยิ่งหากเครื่องมือมีแนวโน้มที่จะถูกใช้โดยเครื่องมืออื่นแบบเปลี่ยนเส้นทาง ชื่อลิงก์สัญลักษณ์ต้องไม่ซ้ำกันในไฟล์รันไทม์ของเครื่องมือและไฟล์ที่ต้องพึ่งพาทั้งหมด
ความครอบคลุมของโค้ด
เมื่อเรียกใช้คำสั่ง coverage
บิลด์อาจต้องเพิ่มเครื่องมือการครอบคลุมสำหรับเป้าหมายบางอย่าง
ยังรวบรวมรายการไฟล์ต้นฉบับที่มีการวัดคุมด้วย กลุ่มเป้าหมายย่อยที่พิจารณาจะควบคุมโดย Flag --instrumentation_filter
ระบบจะยกเว้นเป้าหมายการทดสอบ เว้นแต่จะมีการระบุ --instrument_test_targets
หากการใช้งานกฎเพิ่มเครื่องมือวัดการครอบคลุมขณะสร้าง การใช้งานจะต้องพิจารณาเรื่องนี้ในฟังก์ชันการใช้งาน ctx.coverage_instrumented จะแสดงผลเป็น "จริง" ในโหมดการครอบคลุมหากควรมีเครื่องมือวัดแหล่งที่มาของเป้าหมาย ดังนี้
# Are this rule's sources instrumented?
if ctx.coverage_instrumented():
# Do something to turn on coverage for this compile action
ตรรกะที่ต้องเปิดในโหมดครอบคลุมเสมอ (ไม่ว่าจะเป็นแหล่งที่มาของเป้าหมาย มีการกำหนดเงื่อนไขหรือไม่) สามารถตั้งเงื่อนไข ctx.configuration.coverage_enabled.
หากกฎรวมแหล่งที่มาจากข้อกําหนดก่อนการคอมไพล์โดยตรง (เช่น ไฟล์ส่วนหัว) ก็อาจต้องเปิดเครื่องมือวัดผลขณะคอมไพล์ด้วย หากต้องการตรวจสอบแหล่งที่มาของข้อกําหนด
# Are this rule's sources or any of the sources for its direct dependencies
# in deps instrumented?
if (ctx.configuration.coverage_enabled and
(ctx.coverage_instrumented() or
any([ctx.coverage_instrumented(dep) for dep in ctx.attr.deps]))):
# Do something to turn on coverage for this compile action
กฎควรให้ข้อมูลเกี่ยวกับแอตทริบิวต์ที่เกี่ยวข้องกับ
ความครอบคลุมกับผู้ให้บริการ InstrumentedFilesInfo
ซึ่งสร้างขึ้นโดยใช้
coverage_common.instrumented_files_info
พารามิเตอร์ dependency_attributes
ของ instrumented_files_info
ควรแสดงรายการแอตทริบิวต์การอ้างอิงรันไทม์ทั้งหมด รวมถึงการอ้างอิงโค้ด เช่น deps
และการอ้างอิงข้อมูล เช่น data
พารามิเตอร์ source_attributes
ควรแสดงรายการแอตทริบิวต์ไฟล์ต้นทางของกฎ หากอาจเพิ่มเครื่องมือวัดการครอบคลุม
def _example_library_impl(ctx):
...
return [
...
coverage_common.instrumented_files_info(
ctx,
dependency_attributes = ["deps", "data"],
# Omitted if coverage is not supported for this rule:
source_attributes = ["srcs", "hdrs"],
)
...
]
หากไม่แสดงผล InstrumentedFilesInfo
ระบบจะสร้างรายการเริ่มต้นโดยมีแต่ละรายการ
แอตทริบิวต์ Dependency ที่ไม่ใช่เครื่องมือซึ่งไม่ได้ตั้งค่าไว้
cfg
ไปยัง "host"
หรือ "exec"
ในสคีมาแอตทริบิวต์) ใน
dependency_attributes
(ลักษณะการทำงานนี้ไม่เหมาะ เนื่องจากจะใส่แอตทริบิวต์อย่าง srcs
ใน dependency_attributes
แทน source_attributes
แต่จะช่วยหลีกเลี่ยงการกำหนดค่าการครอบคลุมที่ชัดเจนสำหรับกฎทั้งหมดในเชนการพึ่งพา)
การดำเนินการตรวจสอบ
บางครั้งคุณต้องตรวจสอบบางอย่างเกี่ยวกับบิลด์ และข้อมูลที่จำเป็นในการตรวจสอบนั้นจะมีอยู่ในอาร์ติแฟกต์ (ไฟล์ต้นทางหรือไฟล์ที่สร้างขึ้น) เท่านั้น เนื่องจากข้อมูลนี้อยู่ในอาร์ติแฟกต์ กฎจึงไม่สามารถทำการตรวจสอบนี้ ณ เวลาการวิเคราะห์ได้ เนื่องจากกฎไม่สามารถอ่านไฟล์ได้ โดยจะต้องดำเนินการตรวจสอบนี้ขณะดำเนินการแทน วันและเวลา การตรวจสอบล้มเหลว การดำเนินการจะล้มเหลว และการสร้างก็จะล้มเหลวเช่นกัน
ตัวอย่างการตรวจสอบที่อาจทำงาน ได้แก่ การวิเคราะห์แบบคงที่ การตรวจหาข้อบกพร่อง การตรวจสอบความเชื่อมโยงและความสอดคล้อง และการตรวจสอบรูปแบบ
การตรวจสอบยังช่วยปรับปรุงประสิทธิภาพด้วยการย้ายชิ้นส่วน สำหรับการดำเนินการที่ไม่จำเป็นต่อการสร้างอาร์ติแฟกต์เพื่อการดำเนินการแยกกัน ตัวอย่างเช่น หากการดําเนินการเดียวที่ทําการคอมไพล์และการตรวจวิเคราะห์โค้ดแยกออกเป็นการดำเนินการคอมไพล์และการดำเนินการตรวจวิเคราะห์โค้ดได้ ระบบจะเรียกใช้การดำเนินการตรวจวิเคราะห์โค้ดเป็นการดําเนินการตรวจสอบและเรียกใช้ควบคู่ไปกับการดำเนินการอื่นๆ
"การดำเนินการตรวจสอบ" เหล่านี้มักจะไม่สร้างสิ่งที่ใช้ในที่อื่นในบิลด์ เนื่องจากต้องยืนยันเฉพาะข้อมูลเกี่ยวกับอินพุตเท่านั้น ช่วงเวลานี้ จะแสดงปัญหาบางอย่าง: หากการดำเนินการตรวจสอบไม่สามารถให้ข้อมูล ถูกใช้ในส่วนอื่นของบิลด์ กฎจะเรียกใช้การดำเนินการได้อย่างไร ที่ผ่านมา วิธีการคือให้เอาต์พุตจากการดำเนินการตรวจสอบเป็นค่าว่าง และเพิ่มเอาต์พุตนั้นลงในอินพุตของอินพุตที่สำคัญอื่นๆ การดำเนินการในบิลด์:
วิธีนี้ใช้งานได้เนื่องจาก Bazel จะเรียกใช้การดำเนินการตรวจสอบทุกครั้งที่เรียกใช้การดำเนินการคอมไพล์ แต่มีข้อเสียที่สำคัญดังนี้
การดำเนินการตรวจสอบอยู่ในเส้นทางที่สำคัญของการสร้าง เนื่องจาก Bazel คิดว่าต้องมีเอาต์พุตว่างจึงจะเรียกใช้การดำเนินการคอมไพล์ได้ จึงจะเรียกใช้การดำเนินการตรวจสอบก่อน แม้ว่าการดำเนินการคอมไพล์จะไม่สนใจอินพุตก็ตาม ซึ่งจะลดการทำงานแบบขนานและทำให้บิลด์ช้าลง
หากการดําเนินการอื่นๆ ในบิลด์อาจทํางานแทนการดําเนินการคอมไพล์ ก็จะต้องเพิ่มเอาต์พุตว่างของการดําเนินการตรวจสอบไปยังการดําเนินการเหล่านั้นด้วย (เช่น เอาต์พุต jar แหล่งที่มาของ
java_library
) นี่คือ อาจเกิดปัญหาได้หากมีการดำเนินการใหม่ที่อาจเรียกใช้แทนการดำเนินการคอมไพล์ เพิ่มมาภายหลัง และเอาต์พุตการตรวจสอบที่ว่างเปล่าจะถูกปิดไปโดยไม่ได้ตั้งใจ
วิธีแก้ปัญหาเหล่านี้ก็คือการใช้กลุ่มเอาต์พุตของการตรวจสอบความถูกต้อง
กลุ่มเอาต์พุตของการตรวจสอบ
กลุ่มเอาต์พุตของการตรวจสอบ คือกลุ่มเอาต์พุตที่ออกแบบมาเพื่อเก็บ เอาต์พุตที่ไม่ได้ใช้ของการดำเนินการตรวจสอบ เพื่อที่จะได้ไม่ต้องเกินความเป็นจริง เพิ่มลงในอินพุตของการดำเนินการอื่นๆ แล้ว
กลุ่มนี้มีความพิเศษตรงที่มีการขอเอาต์พุตเสมอ โดยไม่คำนึงถึง
ค่าของ Flag --output_groups
และไม่ว่าเป้าหมายจะเป็นอย่างไรก็ตาม
ขึ้นอยู่กับ (ตัวอย่างเช่น ในบรรทัดคำสั่ง เป็นทรัพยากร Dependency หรือผ่าน
ผลลัพธ์โดยนัยของเป้าหมาย) โปรดทราบว่าการแคชและส่วนเพิ่มปกติ
ยังนำไปใช้ได้: หากอินพุตสำหรับการดำเนินการตรวจสอบไม่มีการเปลี่ยนแปลง และ
การตรวจสอบความถูกต้องก่อนหน้านี้ การยืนยันจะไม่
วิ่งได้
การใช้กลุ่มเอาต์พุตนี้ยังกำหนดให้การดำเนินการตรวจสอบต้องแสดงผลไฟล์บางไฟล์ แม้ว่าจะเป็นไฟล์เปล่าก็ตาม ซึ่งอาจต้องมีการรวมเครื่องมือบางอย่างที่ไม่ได้สร้างเอาต์พุตตามปกติเพื่อให้ระบบสร้างไฟล์
การตรวจสอบความถูกต้องของเป้าหมายจะไม่ทำงานใน 3 กรณีดังนี้
- เมื่อเป้าหมายเป็นเครื่องมือ
- เมื่อเป้าหมายเป็นข้อกําหนดโดยนัย (เช่น แอตทริบิวต์ที่ขึ้นต้นด้วย "_")
- เมื่อมีการสร้างเป้าหมายในการกำหนดค่าโฮสต์หรือการดำเนินการ
โดยสมมติว่าเป้าหมายเหล่านี้มีบิลด์และการทดสอบแยกต่างหากซึ่งจะเปิดเผยความล้มเหลวในการตรวจสอบ
การใช้กลุ่มเอาต์พุตการตรวจสอบ
กลุ่มเอาต์พุตของการตรวจสอบมีชื่อว่า _validation
และใช้งานเหมือนกับกลุ่มอื่นๆ
กลุ่มเอาต์พุต:
def _rule_with_validation_impl(ctx):
ctx.actions.write(ctx.outputs.main, "main output\n")
ctx.actions.write(ctx.outputs.implicit, "implicit output\n")
validation_output = ctx.actions.declare_file(ctx.attr.name + ".validation")
ctx.actions.run(
outputs = [validation_output],
executable = ctx.executable._validation_tool,
arguments = [validation_output.path])
return [
DefaultInfo(files = depset([ctx.outputs.main])),
OutputGroupInfo(_validation = depset([validation_output])),
]
rule_with_validation = rule(
implementation = _rule_with_validation_impl,
outputs = {
"main": "%{name}.main",
"implicit": "%{name}.implicit",
},
attrs = {
"_validation_tool": attr.label(
default = Label("//validation_actions:validation_tool"),
executable = True,
cfg = "exec"),
}
)
โปรดสังเกตว่าไม่มีการเพิ่มไฟล์เอาต์พุตสำหรับการตรวจสอบลงใน DefaultInfo
หรือ
อินพุตไปยังการทำงานอื่นๆ การดำเนินการตรวจสอบสำหรับเป้าหมายของกฎประเภทนี้
จะยังคงทำงานหากเป้าหมายขึ้นอยู่กับป้ายกำกับหรือ
เอาต์พุตโดยนัยขึ้นอยู่กับทั้งทางตรงและทางอ้อม
โดยปกติแล้ว ผลลัพธ์ของการดำเนินการตรวจสอบควรส่งไปยังกลุ่มผลลัพธ์การตรวจสอบเท่านั้น และไม่ควรเพิ่มลงในอินพุตของการดำเนินการอื่นๆ เนื่องจากอาจทำให้เสียประโยชน์จากการทำงานแบบขนาน อย่างไรก็ตาม โปรดทราบว่าขณะนี้ Bazel ยังไม่มีการตรวจสอบพิเศษเพื่อบังคับใช้ ดังนั้นคุณควรทดสอบ จะไม่มีการเพิ่มเอาต์พุตการดำเนินการตรวจสอบลงในอินพุตของการดำเนินการใดๆ ของ Starlark เช่น
load("@bazel_skylib//lib:unittest.bzl", "analysistest")
def _validation_outputs_test_impl(ctx):
env = analysistest.begin(ctx)
actions = analysistest.target_actions(env)
target = analysistest.target_under_test(env)
validation_outputs = target.output_groups._validation.to_list()
for action in actions:
for validation_output in validation_outputs:
if validation_output in action.inputs.to_list():
analysistest.fail(env,
"%s is a validation action output, but is an input to action %s" % (
validation_output, action))
return analysistest.end(env)
validation_outputs_test = analysistest.make(_validation_outputs_test_impl)
Flag การดำเนินการตรวจสอบ
บรรทัดคำสั่ง --run_validations
จะควบคุมการเรียกใช้การตรวจสอบ
ซึ่งมีค่าเริ่มต้นเป็น true
ฟีเจอร์ที่เลิกใช้งาน
เอาต์พุตที่ประกาศล่วงหน้าเลิกใช้งานแล้ว
การใช้เอาต์พุตที่ประกาศไว้ล่วงหน้าซึ่งเลิกใช้งานแล้วมี 2 วิธีดังนี้
พารามิเตอร์
outputs
ของrule
จะระบุการแมประหว่างชื่อแอตทริบิวต์เอาต์พุตกับเทมเพลตสตริงสำหรับสร้างป้ายกำกับเอาต์พุตที่ประกาศไว้ล่วงหน้า แนะนำให้ใช้เอาต์พุตที่ไม่ได้ประกาศไว้ล่วงหน้าและเพิ่มเอาต์พุตลงในDefaultInfo.files
อย่างชัดแจ้ง ใช้ป้ายกำกับของเป้าหมายกฎเป็นอินพุตสำหรับกฎที่ใช้เอาต์พุตแทนป้ายกำกับของเอาต์พุตที่ประกาศไว้ล่วงหน้าสำหรับกฎที่ดำเนินการได้
ctx.outputs.executable
จะอ้างอิง ลงในเอาต์พุตสั่งการที่ประกาศไว้ล่วงหน้า โดยใช้ชื่อเดียวกับเป้าหมายของกฎ คุณควรประกาศเอาต์พุตอย่างชัดเจน เช่นctx.actions.declare_file(ctx.label.name)
และตรวจสอบว่าคำสั่งที่ สร้างไฟล์ปฏิบัติการจะกำหนดสิทธิ์เพื่ออนุญาตการดำเนินการ ส่งเอาต์พุตไฟล์ปฏิบัติการไปยังพารามิเตอร์executable
ของDefaultInfo
อย่างชัดแจ้ง
ฟีเจอร์ Runfiles ที่ควรหลีกเลี่ยง
ctx.runfiles
และ runfiles
มีชุดฟีเจอร์ที่ซับซ้อน ฟีเจอร์หลายฟีเจอร์และหลายรายการจะเก็บไว้ตามเดิม
คำแนะนำต่อไปนี้จะช่วยลดความซับซ้อน
หลีกเลี่ยงการใช้โหมด
collect_data
และcollect_default
ของctx.runfiles
โหมดเหล่านี้จะรวบรวมไฟล์รันไทม์โดยปริยายในขอบเขตของขอบเขตการพึ่งพาที่ฮาร์ดโค้ดไว้บางรายการในลักษณะที่ทำให้เกิดความสับสน แต่ให้เพิ่มไฟล์โดยใช้พารามิเตอร์files
หรือtransitive_files
ของctx.runfiles
หรือการผสานในไฟล์เรียกใช้จากทรัพยากร Dependency ด้วยrunfiles = runfiles.merge(dep[DefaultInfo].default_runfiles)
หลีกเลี่ยงการใช้
data_runfiles
และdefault_runfiles
ของคอนสตรคเตอร์DefaultInfo
โปรดระบุDefaultInfo(runfiles = ...)
แทน ระบบจะยังคงแยกความแตกต่างระหว่างไฟล์รันไทม์ "เริ่มต้น" กับ "ข้อมูล" ไว้เพื่อเหตุผลเดิม ตัวอย่างเช่น กฎบางกฎจะใส่เอาต์พุตเริ่มต้นในdata_runfiles
แต่ไม่ใส่ในdefault_runfiles
แทนที่จะใช้data_runfiles
กฎควรทั้งคู่มีเอาต์พุตเริ่มต้นและผสานdefault_runfiles
จากแอตทริบิวต์ที่มีการเรียกใช้ไฟล์ (บ่อยครั้งdata
)เมื่อดึงข้อมูล
runfiles
จากDefaultInfo
(โดยทั่วไปจะใช้สำหรับการผสานเท่านั้น เรียกใช้ไฟล์ระหว่างกฎปัจจุบันและการอ้างอิง) ให้ใช้DefaultInfo.default_runfiles
ไม่ใช่DefaultInfo.data_runfiles
การย้ายข้อมูลจากผู้ให้บริการเดิม
ก่อนหน้านี้ ผู้ให้บริการ Bazel คือช่องแบบง่ายในออบเจ็กต์ Target
โฆษณาเหล่านี้
มีการเข้าถึงโดยใช้โอเปอเรเตอร์จุด และสร้างขึ้นโดยการใส่ฟิลด์
ในโครงสร้างที่แสดงผลโดยฟังก์ชันการใช้งานของกฎ
สไตล์นี้เลิกใช้งานแล้วและไม่ควรใช้ในโค้ดใหม่ โปรดดูด้านล่างสำหรับ ข้อมูลที่อาจช่วยในการย้ายข้อมูลได้ กลไกผู้ให้บริการใหม่เลี่ยงชื่อ การปะทะกัน นอกจากนี้ยังรองรับการซ่อนข้อมูล โดยกำหนดให้ต้องใช้รหัสที่เข้าถึง ในการเรียกอินสแตนซ์ของผู้ให้บริการ โดยใช้สัญลักษณ์ผู้ให้บริการ
ขณะนี้ระบบยังคงรองรับผู้ให้บริการเดิม กฎสามารถแสดงเป็น ผู้ให้บริการรายเดิมและสมัยใหม่ดังต่อไปนี้
def _old_rule_impl(ctx):
...
legacy_data = struct(x="foo", ...)
modern_data = MyInfo(y="bar", ...)
# When any legacy providers are returned, the top-level returned value is a
# struct.
return struct(
# One key = value entry for each legacy provider.
legacy_info = legacy_data,
...
# Additional modern providers:
providers = [modern_data, ...])
หาก dep
เป็นออบเจ็กต์ Target
ที่เป็นผลลัพธ์สำหรับอินสแตนซ์ของกฎนี้ ค่า
ผู้ให้บริการและเนื้อหาของผู้ให้บริการสามารถดึงข้อมูลเป็น dep.legacy_info.x
และ
dep[MyInfo].y
นอกจาก providers
แล้ว โครงสร้างที่แสดงผลอาจใช้
ช่องต่างๆ ที่มีความหมายพิเศษ (ซึ่งทำให้ไม่ได้สร้าง
ผู้ให้บริการ)
ช่อง
files
,runfiles
,data_runfiles
,default_runfiles
และexecutable
สอดคล้องกับฟิลด์ที่มีชื่อเดียวกันของDefaultInfo
ไม่ได้รับอนุญาตให้ระบุ ข้อมูลเหล่านี้ขณะที่แสดงผลผู้ให้บริการDefaultInfo
ด้วยช่อง
output_groups
ใช้ค่า Struct และสอดคล้องกับOutputGroupInfo
ในการประกาศกฎ provides
และในประกาศแอตทริบิวต์การพึ่งพา providers
ระบบจะส่งผู้ให้บริการเดิมเป็นสตริง และส่งผู้ให้บริการสมัยใหม่ด้วยสัญลักษณ์ *Info
โปรดเปลี่ยนจากสตริงเป็นสัญลักษณ์เมื่อย้ายข้อมูล สําหรับชุดกฎที่ซับซ้อนหรือมีขนาดใหญ่ซึ่งอัปเดตกฎทั้งหมดพร้อมกันได้ยาก คุณอาจทําได้ง่ายขึ้นหากทําตามลําดับขั้นตอนต่อไปนี้
แก้ไขกฎที่สร้างผู้ให้บริการรายเดิมเพื่อสร้างทั้งผู้ให้บริการรายเดิม และผู้ให้บริการที่ทันสมัย โดยใช้ไวยากรณ์ด้านบน สำหรับกฎที่ประกาศว่า ส่งคืนผู้ให้บริการรายเดิม ให้อัปเดตการประกาศนั้นให้รวม ผู้ให้บริการรายเดิมและสมัยใหม่
แก้ไขกฎที่ใช้ผู้ให้บริการเดิมให้ใช้ผู้ให้บริการสมัยใหม่แทน หากการประกาศแอตทริบิวต์ใดๆ ต้องใช้ผู้ให้บริการเดิม ให้อัปเดตการประกาศดังกล่าวให้ต้องใช้ผู้ให้บริการสมัยใหม่แทน คุณอาจสลับขั้นตอนนี้กับขั้นตอนที่ 1 โดยให้ผู้บริโภคยอมรับ/กำหนดผู้ให้บริการรายใดรายหนึ่งก็ได้ โดยทดสอบหาผู้ให้บริการเดิมโดยใช้
hasattr(target, 'foo')
หรือผู้ให้บริการรายใหม่โดยใช้FooInfo in target
นําผู้ให้บริการเดิมออกจากกฎทั้งหมดโดยสมบูรณ์