กฎ

รายงานปัญหา ดูแหล่งที่มา รุ่น Nightly · 7.4 7.3 · 7.2 · 7.1 · 7.0 · 6.5

กฎจะกำหนดชุดการดำเนินการที่ Bazel ดำเนินการกับข้อมูลอินพุตเพื่อสร้างชุดเอาต์พุต ซึ่งจะอ้างอิงในผู้ให้บริการที่แสดงผลโดยฟังก์ชันการใช้งานของกฎ เช่น C++ กฎไบนารีอาจมีลักษณะดังนี้

  1. นำชุดไฟล์ต้นทาง .cpp (อินพุต)
  2. เรียกใช้ g++ ในไฟล์ต้นฉบับ (การดำเนินการ)
  3. แสดงผลผู้ให้บริการ DefaultInfo ที่มีเอาต์พุตที่เรียกใช้งานได้และไฟล์อื่นๆ เพื่อทำให้พร้อมใช้งานขณะรันไทม์
  4. แสดงผลผู้ให้บริการ 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.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
    ...
)

โดยปกติเส้นทางแบบสัมพัทธ์ของไฟล์ในแผนผังการเรียกใช้ไฟล์จะเหมือนกับแท็ก เส้นทางแบบสัมพัทธ์ของไฟล์นั้นในแผนผังแหล่งที่มาหรือแผนผังเอาต์พุตที่สร้างขึ้น หากต้องแตกต่างกันด้วยเหตุผลบางอย่าง คุณสามารถระบุอาร์กิวเมนต์ 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 จะเรียกใช้การดำเนินการตรวจสอบทุกครั้งที่เรียกใช้การดำเนินการคอมไพล์ แต่มีข้อเสียที่สำคัญดังนี้

  1. การดำเนินการตรวจสอบอยู่ในเส้นทางที่สำคัญของการสร้าง เนื่องจาก Bazel คิดว่าต้องมีเอาต์พุตว่างจึงจะเรียกใช้การดำเนินการคอมไพล์ได้ จึงจะเรียกใช้การดำเนินการตรวจสอบก่อน แม้ว่าการดำเนินการคอมไพล์จะไม่สนใจอินพุตก็ตาม ซึ่งจะลดการทำงานแบบขนานและทำให้บิลด์ช้าลง

  2. หากการดําเนินการอื่นๆ ในบิลด์อาจทํางานแทนการดําเนินการคอมไพล์ ก็จะต้องเพิ่มเอาต์พุตว่างของการดําเนินการตรวจสอบไปยังการดําเนินการเหล่านั้นด้วย (เช่น เอาต์พุต 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. แก้ไขกฎที่สร้างผู้ให้บริการรายเดิมเพื่อสร้างทั้งผู้ให้บริการรายเดิม และผู้ให้บริการที่ทันสมัย โดยใช้ไวยากรณ์ด้านบน สำหรับกฎที่ประกาศว่า ส่งคืนผู้ให้บริการรายเดิม ให้อัปเดตการประกาศนั้นให้รวม ผู้ให้บริการรายเดิมและสมัยใหม่

  2. แก้ไขกฎที่ใช้ผู้ให้บริการเดิมให้ใช้ผู้ให้บริการสมัยใหม่แทน หากการประกาศแอตทริบิวต์ใดๆ ต้องใช้ผู้ให้บริการเดิม ให้อัปเดตการประกาศดังกล่าวให้ต้องใช้ผู้ให้บริการสมัยใหม่แทน คุณอาจสลับขั้นตอนนี้กับขั้นตอนที่ 1 โดยให้ผู้บริโภคยอมรับ/กำหนดผู้ให้บริการรายใดรายหนึ่งก็ได้ โดยทดสอบหาผู้ให้บริการเดิมโดยใช้ hasattr(target, 'foo') หรือผู้ให้บริการรายใหม่โดยใช้ FooInfo in target

  3. นําผู้ให้บริการเดิมออกจากกฎทั้งหมดโดยสมบูรณ์