กฎกำหนดชุดการกระทำที่ Bazel ทำ เพื่อสร้างชุดเอาต์พุต ซึ่งมีการอ้างอิงใน providers ที่แสดงโดยแท็ก ฟังก์ชันการใช้งาน เช่น โค้ด C++ กฎไบนารีอาจมีลักษณะดังนี้
- รับชุดไฟล์ต้นฉบับ
.cpp
ไฟล์ (อินพุต) - เรียกใช้
g++
ในไฟล์ต้นฉบับ (การดำเนินการ) - แสดงผลผู้ให้บริการ
DefaultInfo
พร้อมเอาต์พุตสั่งการและไฟล์อื่นๆ ให้พร้อมใช้งานขณะรันไทม์ - แสดงผลผู้ให้บริการ
CcInfo
พร้อมข้อมูลเฉพาะ C++ ที่รวบรวมจาก เป้าหมายและการอ้างอิง
จากมุมมองของ Bazel นั้น g++
และไลบรารี C++ มาตรฐานยังเป็นอินพุตด้วย
ลงในกฎนี้ ในฐานะผู้เขียนกฎ คุณต้องไม่พิจารณาเฉพาะฟีดที่ผู้ใช้ให้ไว้
ข้อมูลลงในกฎ รวมถึงเครื่องมือและไลบรารีทั้งหมดที่จำเป็นต่อการดำเนินการ
การดำเนินการ
ก่อนที่จะสร้างหรือแก้ไขกฎใดๆ โปรดทำความคุ้นเคยกับ สร้างเฟส คุณต้องเข้าใจทั้ง 3 องค์ประกอบ ขั้นตอนในการสร้าง (การโหลด การวิเคราะห์ และการดำเนินการ) และยังมีประโยชน์ในการ เรียนรู้เกี่ยวกับมาโครเพื่อให้เข้าใจความแตกต่างระหว่างกฎต่างๆ มาโคร หากต้องการเริ่มต้น โปรดอ่านบทแนะนำกฎ จากนั้น ให้ใช้หน้านี้เป็นข้อมูลอ้างอิง
Bazel สร้างกฎ 2-3 ข้อขึ้นเอง กฎเนทีฟเหล่านี้ เช่น
cc_library
และ java_binary
จะให้การสนับสนุนหลักสำหรับบางภาษา
การกำหนดกฎเองจะช่วยให้คุณเพิ่มการรองรับภาษาและเครื่องมือที่คล้ายกันได้
ที่ Bazel ไม่สนับสนุนตั้งแต่แรก
Bazel มีโมเดลการขยายสำหรับการเขียนกฎโดยใช้
ภาษา Starlark กฎเหล่านี้เขียนขึ้นในไฟล์ .bzl
ซึ่ง
โหลดได้โดยตรงจาก BUILD
ไฟล์
เมื่อกำหนดกฎของคุณเอง คุณจะต้องเลือกแอตทริบิวต์ที่รองรับและ วิธีสร้างเอาต์พุต
ฟังก์ชัน implementation
ของกฎจะกำหนดลักษณะการทำงานที่แน่นอนระหว่าง
ช่วงการวิเคราะห์ ฟังก์ชันนี้ไม่ได้เรียกใช้
คำสั่งภายนอก แต่จะบันทึกการดำเนินการที่จะใช้
ภายหลังในระหว่างขั้นตอนการดำเนินการเพื่อสร้างเอาต์พุตของกฎ หาก
ที่จำเป็น
การสร้างกฎ
ในไฟล์ .bzl
ให้ใช้ฟังก์ชันกฎเพื่อกำหนด
และจัดเก็บผลลัพธ์ไว้ในตัวแปรร่วม การเรียก rule
จะระบุ
attributes และแอตทริบิวต์
ฟังก์ชันการใช้งาน:
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
attribute คืออาร์กิวเมนต์กฎ แอตทริบิวต์สามารถระบุค่าเฉพาะให้กับ การใช้งานของเป้าหมาย หรืออาจอ้างอิงถึง โดยสร้างกราฟของทรัพยากร 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
เป็นทรัพยากร Dependency ของ my_target
ดังนั้น
other_target
ได้รับการวิเคราะห์ก่อน โดยจะเกิดข้อผิดพลาดหากมีวงจรใน
กราฟทรัพยากร Dependency ของเป้าหมาย
แอตทริบิวต์ส่วนตัวและทรัพยากร Dependency โดยนัย
แอตทริบิวต์ทรัพยากร Dependency ที่มีค่าเริ่มต้นจะสร้างการขึ้นต่อกันแบบไม่เจาะจงปลายทาง ทั้งนี้
เป็นนัยเพราะเป็นส่วนหนึ่งของกราฟเป้าหมายที่ผู้ใช้ไม่ได้
ระบุในไฟล์ BUILD
การขึ้นต่อกันโดยนัยมีประโยชน์สำหรับการฮาร์ดโค้ด
ความสัมพันธ์ระหว่างกฎกับเครื่องมือ (การอ้างอิงเวลาบิลด์ เช่น
คอมไพเลอร์) เนื่องจากโดยส่วนใหญ่ผู้ใช้จะไม่สนใจที่จะระบุ
ที่กฎใช้เท่านั้น ในฟังก์ชันการใช้งานของกฎ พารามิเตอร์นี้
เหมือนกับทรัพยากร 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 โดยนัยจะใช้กับเครื่องมือที่อยู่ภายใน เป็นการใช้งานกฎ หากเครื่องมือมาจาก แพลตฟอร์มการดำเนินการหรือที่เก็บอื่นแทน คุณควรได้รับเครื่องมือนั้นจาก toolchain
แอตทริบิวต์เอาต์พุต
แอตทริบิวต์เอาต์พุต เช่น attr.output
และ
attr.output_list
ให้ประกาศไฟล์เอาต์พุตที่
สร้างได้ตามเป้าหมาย ซึ่งแตกต่างจากแอตทริบิวต์ Dependency ใน 2 ลักษณะ ดังนี้
- พวกเขากำหนดเป้าหมายไฟล์เอาต์พุตแทนที่จะอ้างอิงเป้าหมายที่กำหนด ในที่อื่นๆ
- เป้าหมายไฟล์เอาต์พุตจะขึ้นอยู่กับเป้าหมายของกฎที่สร้างอินสแตนซ์ ไม่ใช่ ในทางกลับกัน
โดยปกติแล้ว แอตทริบิวต์เอาต์พุตจะใช้เมื่อกฎต้องสร้างเอาต์พุตเท่านั้น
ด้วยชื่อที่ผู้ใช้กำหนด ซึ่งไม่สามารถอิงตามชื่อเป้าหมาย หากกฎมี
แอตทริบิวต์เอาต์พุต 1 รายการ โดยทั่วไปจะใช้ชื่อว่า out
หรือ outs
แอตทริบิวต์เอาต์พุตคือวิธีที่แนะนำในการสร้างเอาต์พุตที่ประกาศไว้ล่วงหน้า ขึ้นอยู่กับหรือ ที่ขอในบรรทัดคำสั่ง
ฟังก์ชันการใช้งาน
กฎทุกข้อต้องมีฟังก์ชัน implementation
ฟังก์ชันเหล่านี้จะได้รับการดำเนินการ
ในขั้นการวิเคราะห์อย่างเคร่งครัดและเปลี่ยนรูปแบบ
กราฟของเป้าหมายที่สร้างขึ้นในขั้นตอนการโหลดให้เป็นกราฟของ
การดำเนินการที่ต้องดำเนินการในระยะดำเนินการ ด้วยเหตุนี้
ฟังก์ชันการใช้งานจะไม่สามารถอ่านหรือเขียนไฟล์ได้จริง
ฟังก์ชันการใช้กฎมักจะเป็นแบบส่วนตัว (ตั้งชื่อโดยขึ้นต้นด้วย
ขีดล่าง) ซึ่งโดยทั่วไป จะตั้งชื่อเหมือนกับกฎ แต่ต่อท้ายด้วย
ด้วย _impl
ฟังก์ชันการติดตั้งใช้งานใช้พารามิเตอร์เพียง 1 ตัว ได้แก่
บริบทของกฎ ซึ่งโดยทั่วไปจะใช้ชื่อว่า ctx
โดยแสดงรายการ
providers
เป้าหมาย
การขึ้นต่อกันจะแสดงเป็น 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 รายการเท่านั้น ไฟล์ต้นฉบับไม่สามารถเป็นเอาต์พุตของ
การกระทำใดๆ
สำหรับแอตทริบิวต์ Dependency แต่ละรายการ ช่องที่เกี่ยวข้องของ
ctx.files
มีรายการเอาต์พุตเริ่มต้นจากทั้งหมด
ทรัพยากร Dependency ผ่านแอตทริบิวต์นั้น
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
ถึง สร้างไฟล์จากเทมเพลต
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 กำลังสร้าง การกระทำไม่ได้รับประกันว่าจะได้ดำเนินการ ซึ่งขึ้นอยู่กับว่า ต้องมีเอาต์พุตของบิลด์สำหรับบิลด์
ผู้ให้บริการ
ผู้ให้บริการคือข้อมูลส่วนหนึ่งที่กฎเปิดเผยต่อกฎอื่นๆ ที่ ต้องพึ่งพาสิ่งนั้น ข้อมูลนี้อาจรวมถึงไฟล์เอาต์พุต ไลบรารี และพารามิเตอร์ที่จะส่งผ่าน บรรทัดคำสั่งของเครื่องมือ หรือสิ่งอื่นๆ ที่ผู้บริโภคเป้าหมายควรทราบ เกี่ยวกับ
เนื่องจากฟังก์ชันการใช้งานของกฎจะอ่านได้เฉพาะผู้ให้บริการจาก
ทรัพยากร Dependency ทันทีของเป้าหมายที่สร้างอินสแตนซ์ กฎต้องส่งต่อ
ข้อมูลจากทรัพยากร Dependency ของเป้าหมายที่ปลายทางต้องทราบ
โดยทั่วไปโดยการสะสมจำนวนดังกล่าวลงใน depset
ระบุผู้ให้บริการของเป้าหมายจากรายการออบเจ็กต์ Provider
ที่แสดงโดย
ฟังก์ชันการใช้งาน
ฟังก์ชันการใช้งานแบบเก่ายังสามารถเขียนในรูปแบบเดิมที่
แสดงผลฟังก์ชันการใช้งาน struct
แทนที่จะเป็นรายการ
ออบเจ็กต์ผู้ให้บริการ ไม่สนับสนุนสไตล์นี้อย่างยิ่ง และควรปฏิบัติตามกฎ
อพยพออกจากโดเมนนั้น
เอาต์พุตเริ่มต้น
เอาต์พุตเริ่มต้นของเป้าหมายคือเอาต์พุตที่มีการขอโดยค่าเริ่มต้นเมื่อ
มีการขอเป้าหมายสำหรับบิลด์ในบรรทัดคำสั่ง ตัวอย่างเช่น
java_library
เป้าหมาย //pkg:foo
มี 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
ที่เพิ่มเอาต์พุตไปยัง
เป้าหมาย Runfile ควรรวมไฟล์ 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
และ "ข้อมูลดิบ
ของตัวสร้าง"
ในกรณีนี้ เมื่อมีการเรียกสัญลักษณ์ผู้ให้บริการ แทนที่จะส่งคืนโดยตรง
อินสแตนซ์ใหม่ อินสแตนซ์จะส่งต่ออาร์กิวเมนต์ไปยัง Callback init
ค่าส่งกลับของ 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 executable_file and runfile_file:
runfiles_root = executable_file.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 จึงเกิดแนวคิดเกี่ยวกับ "การกำหนดค่า" และทรานซิชัน เป้าหมายระดับบนสุด (รายการที่ขอในบรรทัดคำสั่ง) จะสร้างขึ้นใน "เป้าหมาย" ขณะที่เครื่องมือที่ควรทำงานบนแพลตฟอร์มการดำเนินการ สร้างขึ้นใน "ผู้บริหาร" การกำหนดค่า กฎอาจสร้างการกระทำที่แตกต่างกันตาม ในการกำหนดค่า เช่น เปลี่ยนสถาปัตยกรรม CPU ที่ส่ง ไปยังคอมไพเลอร์ ในบางกรณี อาจต้องใช้ไลบรารีเดียวกันสำหรับ การกำหนดค่าเอง หากเกิดเหตุการณ์นี้ขึ้น เราจะวิเคราะห์และสร้างสภาพแวดล้อมดังกล่าว หลายครั้ง
โดยค่าเริ่มต้น Bazel จะสร้างทรัพยากร Dependency ของเป้าหมายในการกำหนดค่าเดียวกันกับ ตัวเป้าหมายเอง กล่าวคือไม่มีการเปลี่ยน เมื่อการอ้างอิงเป็น เครื่องมือที่จำเป็นเพื่อช่วยสร้างเป้าหมาย แอตทริบิวต์ที่เกี่ยวข้อง ระบุการเปลี่ยนไปยังการกำหนดค่า exec ซึ่งทำให้เครื่องมือและฟังก์ชัน ทรัพยากร Dependency ที่จะสร้างขึ้นสำหรับแพลตฟอร์มการดำเนินการ
คุณใช้ cfg
สำหรับแอตทริบิวต์ Dependency แต่ละรายการเพื่อเลือกว่าจะใช้ทรัพยากร Dependency หรือไม่
ควรสร้างด้วยการกำหนดค่าเดียวกันหรือเปลี่ยนไปใช้การกำหนดค่าปฏิบัติการ
หากแอตทริบิวต์ทรัพยากร Dependency มีแฟล็ก executable=True
คุณจะต้องตั้งค่า cfg
อย่างชัดเจน เพื่อป้องกันการสร้างเครื่องมือสำหรับผู้ที่ไม่ถูกต้องโดยไม่ได้ตั้งใจ
การกำหนดค่า
ดูตัวอย่าง
โดยทั่วไป ซอร์ส ไลบรารีที่ต้องพึ่งพา และไฟล์ปฏิบัติการซึ่งจำเป็นต้องใช้ที่ ใช้การกำหนดค่าแบบเดียวกันได้
เครื่องมือที่ดำเนินการเป็นส่วนหนึ่งของบิลด์ (เช่น คอมไพเลอร์หรือเครื่องมือสร้างโค้ด)
ควรสร้างสำหรับการกำหนดค่าปฏิบัติการ ในกรณีนี้ ให้ระบุ cfg="exec"
ใน
แอตทริบิวต์
มิเช่นนั้น ไฟล์ปฏิบัติการที่ใช้ขณะรันไทม์ (เช่น เป็นส่วนหนึ่งของการทดสอบ) ควร
สร้างขึ้นสำหรับการกำหนดค่าเป้าหมาย ในกรณีนี้ ให้ระบุ cfg="target"
ใน
แอตทริบิวต์
จริงๆ แล้ว cfg="target"
ไม่ได้ทำอะไรเลย แค่ทำให้รู้สึกสะดวกใจเท่านั้น
ช่วยให้ผู้ออกแบบกฎ
แสดงเจตนาของตนอย่างชัดเจน เมื่อ executable=False
ซึ่งหมายความว่าคุณจะใช้ cfg
หรือไม่ก็ได้ ให้ตั้งค่านี้เมื่อช่วยให้อ่านง่ายขึ้นเท่านั้น
คุณยังใช้ cfg=my_transition
ได้ด้วย
การเปลี่ยนที่ผู้ใช้กําหนด ซึ่งช่วยให้
ผู้เขียนกฎมีความยืดหยุ่นมาก ในการเปลี่ยนแปลงการกำหนดค่า
ข้อเสียของ
ทำให้กราฟบิลด์มีขนาดใหญ่ขึ้นและเข้าใจได้ง่ายขึ้น
หมายเหตุ: ที่ผ่านมา Bazel ไม่มีแนวคิดเรื่องแพลตฟอร์มการดำเนินการ และถือว่าการทำงานของบิลด์ทั้งหมดทำงานในเครื่องโฮสต์แทน ด้วยเหตุนี้ จึงมี "โฮสต์" รายเดียว การกำหนดค่า และ "โฮสต์" การเปลี่ยน ที่ใช้สร้างทรัพยากร Dependency ในการกำหนดค่าโฮสต์ได้ กฎหลายข้อ ยังคงใช้ "โฮสต์" สำหรับเครื่องมือของตน เลิกใช้งานแล้วและกำลังย้ายข้อมูลไปใช้ "exec" การเปลี่ยนฉากเมื่อทำได้
"ผู้จัด" มีความแตกต่างกันอยู่มากมาย และ "exec" การกำหนดค่า:
- "โฮสต์" เป็นเทอร์มินัล, "exec" ไม่ใช่: เมื่อทรัพยากร Dependency อยู่ใน "โฮสต์" การกำหนดค่า ทำให้ไม่สามารถเปลี่ยนได้อีก คุณสร้างรายได้ต่อได้ การเปลี่ยนการกำหนดค่าเมื่อคุณอยู่ใน "exec" การกำหนดค่า
- "โฮสต์" เป็นโมโนลิธ, "exec" ไม่มี: มี "โฮสต์" เพียง 1 ราย การกำหนดค่า แต่สามารถมี "ผู้บริหาร" คนอื่น การกำหนดค่าสำหรับการดำเนินการแต่ละรายการ ที่มีการจัดการครบวงจรได้เลย
- "โฮสต์" จะถือว่าคุณเรียกใช้เครื่องมือบนเครื่องเดียวกับ Bazel หรือบน เครื่องที่คล้ายกันอย่างมีนัยสำคัญ ไม่เป็นความจริงอีกต่อไป คุณสามารถเรียกใช้บิลด์ การดำเนินการในเครื่องของคุณหรือในตัวดำเนินการระยะไกล ซึ่งก็จะไม่มีการดำเนินการ รับประกันว่าผู้ดำเนินการระยะไกลใช้ CPU และระบบปฏิบัติการเดียวกันกับอุปกรณ์ในเครื่อง อุปกรณ์
ทั้ง "ผู้บริหาร" และ "โฮสต์" การกำหนดค่าจะใช้การเปลี่ยนแปลงตัวเลือกเดียวกัน (เช่น
ตั้งค่า --compilation_mode
จาก --host_compilation_mode
ตั้งค่า --cpu
จาก
--host_cpu
เป็นต้น) สิ่งที่แตกต่างกันคือ "โฮสต์" การกำหนดค่าเริ่มต้นด้วย
ค่า default ของ Flag อื่นๆ ทั้งหมด ขณะที่ "exec" การกำหนดค่า
เริ่มต้นด้วยค่าแฟล็กปัจจุบัน โดยอิงตามการกำหนดค่าเป้าหมาย
ส่วนย่อยของการกำหนดค่า
กฎอาจเข้าถึงได้
ส่วนย่อยการกำหนดค่า เช่น
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
...
)
ctx.fragments
มีเฉพาะส่วนย่อยการกำหนดค่าสำหรับเป้าหมาย
การกำหนดค่า ถ้าต้องการเข้าถึงส่วนย่อยสำหรับการกำหนดค่าโฮสต์ ให้ใช้
ctx.host_fragments
แทน
ลิงก์สัญลักษณ์ของ Runfiles
โดยปกติเส้นทางแบบสัมพัทธ์ของไฟล์ในโครงสร้าง Runfiles จะเหมือนกับ
เส้นทางแบบสัมพัทธ์ของไฟล์นั้นในแผนผังแหล่งที่มาหรือแผนผังเอาต์พุตที่สร้างขึ้น หาก
ต้องระบุค่าที่ต่างกันด้วยเหตุผลบางอย่าง คุณสามารถระบุ 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
โปรดระวังอย่าจับคู่
ลงในเส้นทางเดียวกันในโครงสร้าง Runfiles ซึ่งจะทำให้บิลด์ล้มเหลว
มีข้อผิดพลาดที่อธิบายความขัดแย้ง หากต้องการแก้ไข คุณจะต้องแก้ไข
ctx.runfiles
อาร์กิวเมนต์ที่จะนำการชนออก การตรวจสอบนี้จะดำเนินการสำหรับ
เป้าหมายใดๆ ที่ใช้กฎของคุณ รวมทั้งเป้าหมายทุกประเภทที่ขึ้นอยู่กับเป้าหมายเหล่านั้น
เป้าหมาย ซึ่งมีความเสี่ยงอย่างยิ่งหากมีแนวโน้มว่าจะใช้เครื่องมือของคุณแบบทางอ้อม
โดยเครื่องมืออื่น ชื่อลิงก์สัญลักษณ์ต้องไม่ซ้ำกันในทุกการเรียกใช้ไฟล์ของเครื่องมือและ
ทรัพยากร Dependency ทั้งหมด
ความครอบคลุมของโค้ด
เมื่อเรียกใช้คำสั่ง 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
ควรแสดง
แอตทริบิวต์ทรัพยากร Dependency ทั้งหมดรันไทม์ รวมถึงทรัพยากร Dependency ของโค้ด เช่น deps
และ
ทรัพยากร Dependency ของข้อมูล เช่น 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
แต่
ไม่จําเป็นต้องกําหนดค่าการครอบคลุมอย่างชัดแจ้งสําหรับกฎทั้งหมดใน
ห่วงโซ่ทรัพยากร Dependency)
การดำเนินการตรวจสอบ
บางครั้งคุณอาจต้องตรวจสอบบางอย่างเกี่ยวกับบิลด์ และ ข้อมูลที่จำเป็นในการตรวจสอบความถูกต้องจะมีเฉพาะในอาร์ติแฟกต์ (ไฟล์ต้นฉบับหรือไฟล์ที่สร้างขึ้น) เนื่องจากข้อมูลนี้อยู่ในอาร์ติแฟกต์ ไม่สามารถตรวจสอบความถูกต้องนี้ได้ในขณะวิเคราะห์ เนื่องจากกฎไม่สามารถอ่าน โดยจะต้องดำเนินการตรวจสอบนี้ขณะดำเนินการแทน วันและเวลา การตรวจสอบล้มเหลว การดำเนินการจะล้มเหลว และการสร้างก็จะล้มเหลวเช่นกัน
ตัวอย่างของการตรวจสอบที่อาจเรียกใช้ ได้แก่ การวิเคราะห์แบบคงที่ การวิเคราะห์โค้ด การตรวจสอบทรัพยากร Dependency และความสม่ำเสมอ และการตรวจสอบรูปแบบ
การตรวจสอบยังช่วยปรับปรุงประสิทธิภาพด้วยการย้ายชิ้นส่วน สำหรับการดำเนินการที่ไม่จำเป็นต่อการสร้างอาร์ติแฟกต์เพื่อการดำเนินการแยกกัน ตัวอย่างเช่น หากการดำเนินการแบบเดี่ยวที่ทำการคอมไพล์และวิเคราะห์โค้ดได้ แยกออกเป็นการดำเนินการรวมและการทำงานวิเคราะห์โค้ด จากนั้น สามารถดำเนินการเป็นการตรวจสอบความถูกต้อง และเรียกใช้พร้อมกันกับการดำเนินการอื่นๆ
"การดำเนินการตรวจสอบ" เหล่านี้ มักไม่ได้ผลิตสิ่งที่ใช้ในที่อื่น ในบิลด์ เพราะเพียงแค่ต้องยืนยันสิ่งต่างๆ เกี่ยวกับข้อมูลของตัวเองเท่านั้น ช่วงเวลานี้ จะแสดงปัญหาบางอย่าง: หากการดำเนินการตรวจสอบไม่สามารถให้ข้อมูล ถูกใช้ในส่วนอื่นของบิลด์ กฎจะเรียกใช้การดำเนินการได้อย่างไร ที่ผ่านมา วิธีการคือให้เอาต์พุตจากการดำเนินการตรวจสอบเป็นค่าว่าง และเพิ่มเอาต์พุตนั้นลงในอินพุตของอินพุตที่สำคัญอื่นๆ โดยไม่เป็นจริง การดำเนินการในบิลด์:
วิธีนี้ได้ผล เนื่องจาก Bazel จะเรียกใช้การดำเนินการตรวจสอบเสมอเมื่อคอมไพล์ เรียกใช้ แต่มีข้อเสียที่สำคัญดังนี้
การดำเนินการตรวจสอบอยู่ในเส้นทางวิกฤติของบิลด์ เนื่องจากบาเซล คิดว่าเอาต์พุตที่ว่างเปล่าจำเป็นต้องเรียกใช้การดำเนินการคอมไพล์ โค้ดจะเรียกใช้ การดำเนินการตรวจสอบก่อน แม้ว่าการดำเนินการคอมไพล์จะไม่สนใจอินพุตก็ตาม การทำเช่นนี้จะลดการทำงานพร้อมกันและทำให้บิลด์ช้าลง
หากการดำเนินการอื่นๆ ในบิลด์อาจทำงานแทน คอมไพล์ได้ จึงต้องเพิ่มเอาต์พุตที่ว่างเปล่าของการดำเนินการตรวจสอบลงใน การดำเนินการเหล่านั้นด้วย (เช่น เอาต์พุต Jar ต้นทางของ
java_library
) นี่คือ อาจเกิดปัญหาเช่นกัน หากการดำเนินการใหม่ที่อาจทำงานแทนการดำเนินการคอมไพล์ เพิ่มมาภายหลัง และเอาต์พุตการตรวจสอบที่ว่างเปล่าจะถูกปิดไปโดยไม่ได้ตั้งใจ
วิธีแก้ปัญหาเหล่านี้ก็คือการใช้กลุ่มเอาต์พุตของการตรวจสอบความถูกต้อง
กลุ่มเอาต์พุตของการตรวจสอบ
กลุ่มเอาต์พุตของการตรวจสอบ คือกลุ่มเอาต์พุตที่ออกแบบมาเพื่อเก็บ เอาต์พุตที่ไม่ได้ใช้ของการดำเนินการตรวจสอบ เพื่อที่จะได้ไม่ต้องเกินความเป็นจริง เพิ่มลงในอินพุตของการดำเนินการอื่นๆ แล้ว
กลุ่มนี้มีความพิเศษตรงที่มีการขอเอาต์พุตเสมอ โดยไม่คำนึงถึง
ค่าของ Flag --output_groups
และไม่ว่าเป้าหมายจะเป็นอย่างไรก็ตาม
ขึ้นอยู่กับ (ตัวอย่างเช่น ในบรรทัดคำสั่ง เป็นทรัพยากร Dependency หรือผ่าน
ผลลัพธ์โดยนัยของเป้าหมาย) โปรดทราบว่าการแคชและส่วนเพิ่มปกติ
ยังนำไปใช้ได้: หากอินพุตของการดำเนินการตรวจสอบไม่มีการเปลี่ยนแปลง และ
การตรวจสอบความถูกต้องก่อนหน้านี้ การยืนยันจะไม่
วิ่งได้
การใช้กลุ่มเอาต์พุตนี้ยังคงกำหนดให้การดำเนินการตรวจสอบจะแสดงไฟล์บางไฟล์ แม้แต่รายการที่ว่างเปล่า ซึ่งอาจต้องมีการรวมเครื่องมือบางอย่างที่โดยปกติแล้วไม่มี สร้างเอาต์พุตเพื่อให้สร้างไฟล์
การตรวจสอบความถูกต้องของเป้าหมายจะไม่ทำงานใน 3 กรณีดังนี้
- เมื่อมีการพึ่งพาเป้าหมายในฐานะเครื่องมือ
- เมื่อเป้าหมายมีการอ้างอิงเป็นทรัพยากร Dependency โดยนัย (ตัวอย่างเช่น แอตทริบิวต์ที่ขึ้นต้นด้วย "_")
- เมื่อมีการสร้างเป้าหมายในการกำหนดค่าโฮสต์หรือการดำเนินการ
คาดว่าเป้าหมายเหล่านี้มี แยกบิลด์และการทดสอบที่อาจพบความล้มเหลวในการตรวจสอบความถูกต้อง
การใช้กลุ่มเอาต์พุตของการตรวจสอบความถูกต้อง
กลุ่มเอาต์พุตของการตรวจสอบมีชื่อว่า _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
การประกาศทรัพยากร Dependency
ผู้ให้บริการเดิมจะส่งผ่านเป็นสตริง และผู้ให้บริการสมัยใหม่คือ
ส่งมาจากสัญลักษณ์ *Info
อย่าลืมเปลี่ยนจากสตริงเป็นสัญลักษณ์
เมื่อย้ายข้อมูล สำหรับชุดกฎที่ซับซ้อนหรือมีขนาดใหญ่ซึ่งอัปเดตได้ยาก
ตามกฎต่างๆ ทีละส่วน คุณอาจประหยัดเวลา
หากคุณทำตามลำดับของ
ขั้นตอน:
แก้ไขกฎที่สร้างผู้ให้บริการรายเดิมเพื่อสร้างทั้งผู้ให้บริการรายเดิม และผู้ให้บริการที่ทันสมัย โดยใช้ไวยากรณ์ด้านบน สำหรับกฎที่ประกาศว่า ส่งคืนผู้ให้บริการรายเดิม ให้อัปเดตการประกาศนั้นให้รวม ผู้ให้บริการรายเดิมและสมัยใหม่
แก้ไขกฎที่ใช้ผู้ให้บริการเดิมเพื่อใช้พารามิเตอร์ ผู้ให้บริการที่ทันสมัย หากการประกาศแอตทริบิวต์ต้องใช้ผู้ให้บริการเดิม ควรอัปเดตการตั้งค่าให้ใช้ผู้ให้บริการที่ทันสมัยแทน หรือคุณสามารถ แทรกสลับการทำงานนี้ในขั้นตอนที่ 1 โดยให้ผู้บริโภคยอมรับ/กำหนดให้ดำเนินการอย่างใดอย่างหนึ่ง ผู้ให้บริการ: ทดสอบการแสดงผู้ให้บริการเดิมโดยใช้
hasattr(target, 'foo')
หรือผู้ให้บริการรายใหม่ที่ใช้FooInfo in target
นําผู้ให้บริการเดิมออกจากกฎทั้งหมดโดยสมบูรณ์