กฎกำหนดชุดการกระทำที่ Bazel ทำ เพื่อสร้างชุดเอาต์พุต ซึ่งมีการอ้างอิงใน providers ที่แสดงโดยแท็ก ฟังก์ชันการใช้งาน เช่น กฎไบนารี C++ อาจมีลักษณะดังนี้
- รับชุดไฟล์ต้นฉบับ
.cpp
ไฟล์ (อินพุต) - เรียกใช้
g++
ในไฟล์ต้นฉบับ (การดำเนินการ) - แสดงผลผู้ให้บริการ
DefaultInfo
พร้อมเอาต์พุตสั่งการและไฟล์อื่นๆ ให้พร้อมใช้งานขณะรันไทม์ - แสดงผลผู้ให้บริการ
CcInfo
พร้อมข้อมูลเฉพาะ C++ ที่รวบรวมจาก เป้าหมายและการอ้างอิง
จากมุมมองของ Bazel g++
และไลบรารี C++ มาตรฐานก็เป็นอินพุตของกฎนี้ด้วย ในฐานะผู้เขียนกฎ คุณต้องพิจารณาไม่เพียงอินพุตที่ผู้ใช้ระบุให้กับกฎเท่านั้น แต่ยังต้องพิจารณาเครื่องมือและไลบรารีทั้งหมดที่จําเป็นต่อการดำเนินการด้วย
ก่อนสร้างหรือแก้ไขกฎใดๆ โปรดตรวจสอบว่าคุณคุ้นเคยกับระยะการสร้างของ Bazel คุณควรทำความเข้าใจ 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=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 ของโค้ดสำหรับเป้าหมาย สคีมาแอตทริบิวต์ควรระบุผู้ให้บริการที่ต้องมีทรัพยากร 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),
...
},
)
ตัวอย่างแอตทริบิวต์ Dependency แอตทริบิวต์ที่ระบุป้ายกำกับอินพุต (ซึ่งกำหนดด้วย attr.label_list
, attr.label
หรือ attr.label_keyed_string_dict
) จะระบุการพึ่งพาของประเภทหนึ่งระหว่างเป้าหมายกับเป้าหมายที่มีป้ายกำกับ (หรือออบเจ็กต์ 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
มี Implicit
ขึ้นอยู่กับคอมไพเลอร์ //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
ซึ่งจะแสดงรายการผู้ให้บริการ
เป้าหมาย
ระบบจะแสดงข้อมูลพึ่งพาเป็นออบเจ็กต์ Target
ในเวลาที่วิเคราะห์ ออบเจ็กต์เหล่านี้มี providers ที่สร้างขึ้นเมื่อ
เรียกใช้ฟังก์ชันการใช้งานของเป้าหมายแล้ว
ctx.attr
มีช่องที่สอดคล้องกับชื่อของแต่ละ
แอตทริบิวต์ Dependency ที่มีออบเจ็กต์ Target
รายการที่แสดงถึงโดยตรงแต่ละรายการ
Dependency ผ่านแอตทริบิวต์นั้น สำหรับแอตทริบิวต์ 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 และ get hello.o" เมื่อมีการสร้างการกระทำขึ้น Bazel จะไม่เรียกใช้คำสั่งโดยทันที โดยจะบันทึกไว้ในกราฟของทรัพยากร Dependency เนื่องจากการดำเนินการหนึ่งๆ จะขึ้นอยู่กับเอาต์พุตของการดำเนินการอื่น ตัวอย่างเช่น ใน C ต้องเรียก linker หลังคอมไพเลอร์
ฟังก์ชันวัตถุประสงค์ทั่วไปที่สร้างการกระทำจะกำหนดไว้ใน
ctx.actions
ctx.actions.run
เพื่อเรียกใช้ไฟล์ปฏิบัติการctx.actions.run_shell
เพื่อเรียกใช้คำสั่งเชลล์ctx.actions.write
เพื่อเขียนสตริงลงในไฟล์ctx.actions.expand_template
เพื่อ generate ไฟล์จากเทมเพลต
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],
)
...
การดำเนินการจะนํารายการหรือชุดไฟล์อินพุตมาสร้างรายการไฟล์เอาต์พุต (ที่ไม่ใช่ไฟล์ว่าง) ต้องทราบชุดของไฟล์อินพุตและเอาต์พุตระหว่าง การวิเคราะห์ ซึ่งอาจขึ้นอยู่กับค่าของแอตทริบิวต์ รวมถึงผู้ให้บริการจากข้อกําหนด แต่ต้องไม่ขึ้นอยู่กับผลลัพธ์ของการดําเนินการ เช่น หากการดำเนินการเรียกใช้คำสั่ง unzip คุณต้องระบุไฟล์ที่คุณต้องการขยาย (ก่อนเรียกใช้ unzip) การดำเนินการที่สร้างไฟล์ภายในจํานวนไม่คงที่สามารถรวมไฟล์เหล่านั้นไว้ในไฟล์เดียวได้ (เช่น 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]), ...),
...
]
หากการใช้กฎหรือ files
ไม่แสดงผล DefaultInfo
ไม่ได้ระบุพารามิเตอร์ มีค่าเริ่มต้น DefaultInfo.files
รายการเป็นทั้งหมด
เอาต์พุตที่ประกาศล่วงหน้า (โดยทั่วไปคือเอาต์พุตที่สร้างโดยเอาต์พุต
แอตทริบิวต์)
กฎที่ดำเนินการต่างๆ ควรมีเอาต์พุตเริ่มต้น แม้ว่าจะมีเอาต์พุตเหล่านั้นก็ตาม ไม่มีการใช้โดยตรง การดำเนินการที่ไม่ได้อยู่ในกราฟของ เอาต์พุตที่ขอถูกตัดทอน หากเอาต์พุตมีไว้สําหรับผู้ใช้เป้าหมายเท่านั้น ระบบจะไม่ดําเนินการเหล่านั้นเมื่อสร้างเป้าหมายแยกต่างหาก ซึ่งทําให้การแก้ไขข้อบกพร่องยากขึ้น เนื่องจากการสร้างเป้าหมายที่ทํางานไม่สําเร็จขึ้นใหม่จะไม่ทําให้เกิดความล้มเหลวซ้ำ
ไฟล์รันไทม์
Runfile คือชุดไฟล์ที่เป้าหมายใช้ขณะรันไทม์ (แทนที่จะเป็นบิลด์ เวลา) ในระหว่างระยะการดําเนินการ Bazel จะสร้างต้นไม้ไดเรกทอรีที่มีลิงก์สัญลักษณ์ซึ่งชี้ไปยังไฟล์รันไทม์ ซึ่งจะจัดเตรียมสภาพแวดล้อมสําหรับไบนารีเพื่อให้เข้าถึงไฟล์รันไทม์ได้ในระหว่างรันไทม์
คุณสามารถเพิ่มไฟล์เรียกใช้ด้วยตนเองระหว่างการสร้างกฎ
สามารถสร้างออบเจ็กต์ runfiles
โดยใช้เมธอด runfiles
ในบริบทกฎ ctx.runfiles
และส่งไปยังพารามิเตอร์ runfiles
ใน DefaultInfo
ระบบจะเพิ่มเอาต์พุตที่เรียกใช้งานได้ของกฎที่เรียกใช้งานได้ลงในไฟล์เรียกใช้โดยปริยาย
กฎบางอย่างจะระบุแอตทริบิวต์ ซึ่งโดยทั่วไปจะใช้ชื่อว่า
data
ที่เพิ่มเอาต์พุตไปยัง
เป้าหมาย runfiles. ควรรวมไฟล์ Runfile จาก 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
(ควรทดสอบชื่อ target ด้วยเช่นกัน
จะลงท้ายด้วย _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 เป็นพารามิเตอร์โดยใช้สภาพแวดล้อมหรือบรรทัดคำสั่ง
อาร์กิวเมนต์/แฟล็ก ซึ่งช่วยให้ไบนารีสามารถส่งรูทไฟล์รันไทม์ตามหลักเกณฑ์ที่ถูกต้องให้กับไบนารีที่เรียกใช้ หากไม่ได้ตั้งค่า ไบนารีจะสามารถเดาได้ว่านี่เป็น
ไบนารีแรกจะเรียกและมองหาไดเรกทอรี 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
สำหรับแอตทริบิวต์ Dependency แต่ละรายการเพื่อเลือกว่าจะใช้ทรัพยากร Dependency หรือไม่
ควรสร้างด้วยการกำหนดค่าเดียวกันหรือเปลี่ยนไปใช้การกำหนดค่าปฏิบัติการ
หากแอตทริบิวต์ทรัพยากร Dependency มีแฟล็ก executable=True
คุณจะต้องตั้งค่า cfg
อย่างชัดเจน การดำเนินการนี้เพื่อป้องกันการสร้างเครื่องมือสำหรับการกำหนดค่าที่ไม่ถูกต้องโดยไม่ตั้งใจ
ดูตัวอย่าง
โดยทั่วไปแล้ว แหล่งที่มา ไลบรารีที่เกี่ยวข้อง และไฟล์ปฏิบัติการที่ต้องใช้เมื่อรันไทม์จะใช้การกำหนดค่าเดียวกันได้
เครื่องมือที่ดำเนินการเป็นส่วนหนึ่งของบิลด์ (เช่น คอมไพเลอร์หรือเครื่องมือสร้างโค้ด)
ควรสร้างสำหรับการกำหนดค่าปฏิบัติการ ในกรณีนี้ ให้ระบุ 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
บิลด์อาจต้องเพิ่มเครื่องมือการครอบคลุมสำหรับเป้าหมายบางอย่าง บิลด์ยังรวบรวมรายการไฟล์ต้นทางที่เครื่องมือวัดด้วย เซ็ตย่อยของ
เป้าหมายที่ถือว่าเป็นการควบคุมโดยการตั้งค่าสถานะ
--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.
หากกฎรวมแหล่งที่มาจากทรัพยากร Dependency โดยตรงก่อนการคอมไพล์ (เช่น ไฟล์ส่วนหัว) คุณอาจจำเป็นต้องเปิดการวัดคุมเวลาคอมไพล์ หาก ทรัพยากร Dependency ' แหล่งที่มาควรมีเครื่องมือ:
# 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
ระบบจะสร้าง InstrumentedFilesInfo
เริ่มต้นขึ้นโดยมีค่าแอตทริบิวต์ความเกี่ยวข้องที่ไม่ใช่เครื่องมือแต่ละรายการ (ที่ไม่ได้ตั้งค่า cfg
เป็น "host"
หรือ "exec"
ในสคีมาแอตทริบิวต์) ใน dependency_attributes
(ลักษณะการทำงานนี้ไม่เหมาะ เนื่องจากจะใส่แอตทริบิวต์อย่าง srcs
ใน dependency_attributes
แทน source_attributes
แต่จะช่วยหลีกเลี่ยงการกำหนดค่าการครอบคลุมที่ชัดเจนสำหรับกฎทั้งหมดในเชนการพึ่งพา)
การดำเนินการตรวจสอบ
บางครั้งคุณต้องตรวจสอบบางอย่างเกี่ยวกับบิลด์ และข้อมูลที่จำเป็นในการตรวจสอบนั้นจะมีอยู่ในอาร์ติแฟกต์ (ไฟล์ต้นทางหรือไฟล์ที่สร้างขึ้น) เท่านั้น เนื่องจากข้อมูลนี้อยู่ในอาร์ติแฟกต์ กฎจึงไม่สามารถทำการตรวจสอบนี้ ณ เวลาการวิเคราะห์ได้ เนื่องจากกฎไม่สามารถอ่านไฟล์ได้ แต่การดำเนินการต้องดำเนินการตรวจสอบนี้ ณ เวลาดำเนินการ เมื่อการตรวจสอบไม่สำเร็จ การดำเนินการก็จะไม่สำเร็จ และบิลด์ก็จะไม่สำเร็จด้วย
ตัวอย่างของการตรวจสอบที่อาจเรียกใช้ ได้แก่ การวิเคราะห์แบบคงที่ การวิเคราะห์โค้ด การตรวจสอบทรัพยากร Dependency และความสม่ำเสมอ และการตรวจสอบรูปแบบ
การดำเนินการตรวจสอบยังช่วยปรับปรุงประสิทธิภาพการสร้างได้โดยย้ายการดำเนินการบางส่วนที่ไม่จำเป็นต่อการสร้างอาร์ติแฟกต์ไปยังการดำเนินการแยกต่างหาก ตัวอย่างเช่น หากการดําเนินการเดียวที่ทําการคอมไพล์และการตรวจวิเคราะห์โค้ดแยกออกเป็นการดำเนินการคอมไพล์และการดำเนินการตรวจวิเคราะห์โค้ดได้ ระบบจะเรียกใช้การดำเนินการตรวจวิเคราะห์โค้ดเป็นการดําเนินการตรวจสอบและเรียกใช้ควบคู่ไปกับการดำเนินการอื่นๆ
"การดำเนินการตรวจสอบ" เหล่านี้มักจะไม่สร้างสิ่งที่ใช้ในที่อื่นในบิลด์ เนื่องจากต้องยืนยันเฉพาะข้อมูลเกี่ยวกับอินพุตเท่านั้น ช่วงเวลานี้ จะแสดงปัญหาบางอย่าง: หากการดำเนินการตรวจสอบไม่สามารถให้ข้อมูล ถูกใช้ในส่วนอื่นของบิลด์ กฎจะเรียกใช้การดำเนินการได้อย่างไร ที่ผ่านมา วิธีการคือให้เอาต์พุตจากการดำเนินการตรวจสอบเป็นค่าว่าง และเพิ่มเอาต์พุตนั้นลงในอินพุตของอินพุตที่สำคัญอื่นๆ การดำเนินการในบิลด์:
วิธีนี้ใช้งานได้เนื่องจาก 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)
แฟล็กการดำเนินการตรวจสอบ
บรรทัดคำสั่ง --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
นําผู้ให้บริการเดิมออกจากกฎทั้งหมดโดยสมบูรณ์