จัดการทรัพยากร Dependency ภายนอกด้วย Bzlmod

รายงานปัญหา ดูแหล่งที่มา /3} /4} {3/4} {3/4} {3/4} {3/4} /4.

Bzlmod เป็นชื่อรหัสของระบบทรัพยากร Dependency ภายนอกแบบใหม่ที่เปิดตัวใน Bazel 5.0 คำแนะนำนี้มาจากการแก้ไขจุดด้อยต่างๆ ของระบบเก่าที่แก้ไขไม่ได้ทีละอย่าง ดูรายละเอียดเพิ่มเติมได้ที่ส่วนคำแถลงปัญหาในเอกสารออกแบบต้นฉบับ

ใน Bazel 5.0 จะไม่มีการเปิดใช้ Bzlmod โดยค่าเริ่มต้น และต้องระบุ Flag --experimental_enable_bzlmod เพื่อให้รายการต่อไปนี้มีผล อย่างที่ชื่อแฟล็กนั้นบอกไว้ ฟีเจอร์นี้ยังอยู่ในช่วงทดลอง API และลักษณะการทำงานอาจมีการเปลี่ยนแปลงจนกว่าฟีเจอร์จะเปิดตัวอย่างเป็นทางการ

หากต้องการย้ายข้อมูลโปรเจ็กต์ไปยัง Bzlmod ให้ทำตามคู่มือการย้ายข้อมูล Bzlmod นอกจากนี้ คุณยังดูตัวอย่างการใช้ Bzlmod ได้ในที่เก็บ examples

โมดูล Bazel

ระบบทรัพยากร Dependency ภายนอกแบบเก่าซึ่งใช้ WORKSPACE เป็นศูนย์กลางของที่เก็บ (หรือที่เก็บ) ซึ่งสร้างขึ้นผ่านกฎที่เก็บ (หรือกฎที่เก็บ) แม้ว่าที่เก็บจะยังคงเป็นแนวคิดที่สำคัญในระบบใหม่ แต่โมดูลคือหน่วยหลักของทรัพยากร Dependency

โดยพื้นฐานแล้ว โมดูลคือโปรเจ็กต์ Bazel ที่มีได้หลายเวอร์ชัน โดยแต่ละเวอร์ชันจะเผยแพร่ข้อมูลเมตาเกี่ยวกับโมดูลอื่นๆ ที่ต้องใช้อยู่ ซึ่งคล้ายกับแนวคิดที่คุ้นเคยในระบบการจัดการทรัพยากร Dependency อื่นๆ เช่น อาร์ติแฟกต์ของ Maven, แพ็กเกจ npm, ลัง Cargo, โมดูล Go เป็นต้น

โมดูลจะระบุทรัพยากร Dependency โดยใช้คู่ name และ version แทน URL ที่เฉพาะเจาะจงใน WORKSPACE จากนั้นจะค้นหาทรัพยากร Dependency ในรีจิสทรีของ Bazel โดยค่าเริ่มต้นคือ Bazel Central Registry ในพื้นที่ทำงาน แต่ละโมดูล จะกลายเป็นที่เก็บ

MODULE.bazel

ทุกเวอร์ชันของทุกโมดูลมีไฟล์ MODULE.bazel ที่ประกาศการขึ้นต่อกันและข้อมูลเมตาอื่นๆ ต่อไปนี้คือตัวอย่างพื้นฐาน

module(
    name = "my-module",
    version = "1.0",
)

bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")

ไฟล์ MODULE.bazel ควรอยู่ที่รูทของไดเรกทอรีพื้นที่ทำงาน (ถัดจากไฟล์ WORKSPACE) ซึ่งต่างจากไฟล์ WORKSPACE คุณไม่จำเป็นต้องระบุทรัพยากร Dependency แบบสับเปลี่ยน แต่ควรระบุเฉพาะทรัพยากร Dependency โดยตรง แทน โดยระบบจะประมวลผลไฟล์ MODULE.bazel ของทรัพยากร Dependency เพื่อค้นหาทรัพยากร Dependency แบบทรานซิทีฟโดยอัตโนมัติ

ไฟล์ MODULE.bazel คล้ายกับไฟล์ BUILD เนื่องจากไม่รองรับโฟลว์การควบคุมรูปแบบใดๆ และไม่อนุญาตคำสั่ง load ด้วย คำสั่งที่รองรับไฟล์ MODULE.bazel มีดังนี้

  • module เพื่อระบุข้อมูลเมตาเกี่ยวกับโมดูลปัจจุบัน รวมถึงชื่อ เวอร์ชัน และอื่นๆ
  • bazel_dep เพื่อระบุการขึ้นต่อกันโดยตรงในโมดูล Bazel อื่นๆ
  • การลบล้างซึ่งใช้ได้เฉพาะโดยโมดูลรูท (ไม่ใช่โดยโมดูลที่ใช้เป็นทรัพยากร Dependency) เพื่อปรับแต่งลักษณะการทำงานของทรัพยากร Dependency โดยตรงหรือทรัพยากร Dependency บางอย่างเท่านั้น
  • คำสั่งที่เกี่ยวข้องกับส่วนขยายโมดูลมีดังนี้

รูปแบบเวอร์ชัน

Bazel มีระบบนิเวศที่หลากหลาย และโปรเจ็กต์ต่างๆ ก็ใช้รูปแบบการกำหนดเวอร์ชันที่หลากหลาย ความนิยมมากที่สุดในปัจจุบันคือ SemVer แต่ก็ยังมีโปรเจ็กต์ที่โดดเด่นซึ่งใช้รูปแบบที่แตกต่างออกไป เช่น Abseil ซึ่งเวอร์ชันอิงตามวันที่ เช่น 20210324.2)

ด้วยเหตุนี้ Bzlmod จึงใช้ข้อกำหนดของ SemVer เวอร์ชันที่ผ่อนคลายมากกว่า โดยมีความแตกต่างดังนี้

  • SemVer ระบุว่าส่วน "รุ่น" ของเวอร์ชันต้องประกอบด้วย 3 กลุ่มคือ MAJOR.MINOR.PATCH ใน Bazel ข้อกำหนดนี้จะถูกผ่อนปรน เพื่อให้สามารถใช้กลุ่มได้ไม่จำกัดจำนวน
  • ใน SemVer แต่ละส่วนในส่วน "release" ต้องเป็นตัวเลขเท่านั้น ใน Bazel คุณจะกำหนดได้โดยใช้ตัวอักษรเช่นกัน และความหมายการเปรียบเทียบจะตรงกับ "ตัวระบุ" ในส่วน "ล่วงหน้า"
  • นอกจากนี้ จะไม่มีการบังคับใช้ความหมายของการเพิ่มเวอร์ชันหลัก ย่อย และเวอร์ชันแพตช์ (อย่างไรก็ตาม โปรดดูระดับความเข้ากันได้สำหรับรายละเอียด เกี่ยวกับวิธีที่เราแสดงความเข้ากันได้แบบย้อนหลัง)

เวอร์ชัน SemVer ที่ถูกต้องคือเวอร์ชันโมดูล Bazel ที่ถูกต้อง นอกจากนี้จะมี SemVer 2 เวอร์ชัน a และ b เปรียบเทียบ a < b กับการคงไว้ชั่วคราวเดียวกันเมื่อเปรียบเทียบกับเวอร์ชันโมดูล Bazel

ความละเอียดเวอร์ชัน

ปัญหาการขึ้นต่อกันแบบไดมอนด์เป็นองค์ประกอบหลักของพื้นที่การจัดการทรัพยากร Dependency ที่มีเวอร์ชัน สมมติว่าคุณมีกราฟทรัพยากร Dependency ต่อไปนี้

       A 1.0
      /     \
   B 1.0    C 1.1
     |        |
   D 1.0    D 1.1

ควรใช้ D เวอร์ชันใด Bzlmod ใช้อัลกอริทึมการเลือกเวอร์ชันขั้นต่ำ (MVS) ที่เปิดตัวในระบบโมดูล Go เพื่อตอบคำถามนี้ MVS สันนิษฐานว่าโมดูลเวอร์ชันใหม่ทั้งหมดสามารถใช้งานร่วมกันได้แบบย้อนหลัง จึงเพียงแต่เลือกเวอร์ชันสูงสุดที่ระบุโดยจะขึ้นอยู่กับเวอร์ชันใดก็ได้ (D 1.1 ในตัวอย่างของเรา) เราเรียกค่าดังกล่าวว่า "ค่าต่ำสุด" เนื่องจาก D 1.1 ในที่นี้เป็นเวอร์ชันขั้นต่ำที่สามารถปฏิบัติตามข้อกำหนดของเรา แม้ว่า D 1.2 ขึ้นไปจะมีอยู่แล้ว เราจะไม่เลือกเวอร์ชันดังกล่าว ซึ่งมีประโยชน์เพิ่มเติมที่ทำให้การเลือกเวอร์ชันมีความแม่นยำสูงและทำซ้ำได้

การแก้ปัญหาเวอร์ชันจะดำเนินการในเครื่องของคุณ ไม่ใช่จากรีจิสทรี

ระดับความเข้ากันได้

โปรดทราบว่าสมมติฐานของ MVS เกี่ยวกับความเข้ากันได้แบบย้อนหลังนั้นเป็นไปได้เนื่องจากเพียงแค่ถือว่าโมดูลเวอร์ชันที่ใช้ร่วมกันไม่ได้แบบย้อนหลังเป็นโมดูลแยกต่างหาก ในแง่ของ SemVer หมายความว่า A 1.x และ A 2.x ถือเป็นโมดูลที่แยกจากกัน และอยู่ร่วมกันในกราฟทรัพยากร Dependency ได้ ซึ่งก็เป็นไปได้เช่นกันว่าเวอร์ชันหลักมีการเข้ารหัสในเส้นทางแพ็กเกจใน Go เพื่อไม่ให้เกิดความขัดแย้งเวลาคอมไพล์หรือเวลาลิงก์

เราไม่ได้รับการรับรองใน Bazel เราจึงต้องมีวิธีที่จะระบุหมายเลข "เวอร์ชันหลัก" เพื่อตรวจหาเวอร์ชันที่ใช้ร่วมกันไม่ได้ย้อนหลัง หมายเลขนี้เรียกว่าระดับความเข้ากันได้ และระบุโดยเวอร์ชันโมดูลแต่ละเวอร์ชันในคำสั่ง module() เมื่อมีข้อมูลนี้อยู่ในมือ เราอาจแสดงข้อผิดพลาดเมื่อตรวจพบว่ามีโมดูลเดียวกันเวอร์ชันต่างๆ ที่มีระดับความเข้ากันได้แตกต่างกันในกราฟทรัพยากร Dependency ที่ได้รับการแก้ไขแล้ว

ชื่อที่เก็บ

ทรัพยากร Dependency ภายนอกทุกรายการใน Bazel จะมีชื่อที่เก็บ บางครั้งการขึ้นต่อกันเดียวกันอาจใช้ผ่านชื่อที่เก็บที่ต่างกัน (เช่น ทั้ง @io_bazel_skylib และ @bazel_skylib หมายถึง Bazel skylib) หรืออาจใช้ชื่อที่เก็บเดียวกันสำหรับทรัพยากร Dependency ที่แตกต่างกันในโปรเจ็กต์ที่ต่างกัน

ใน Bzlmod โมดูล Bazel และส่วนขยายโมดูลจะสร้างที่เก็บได้ หากต้องการแก้ไขความขัดแย้งของชื่อที่เก็บ เราจะใช้กลไกการแมปที่เก็บในระบบใหม่ แนวคิดสำคัญมี 2 ประการดังนี้

  • ชื่อที่เก็บ Canonical: ชื่อที่เก็บที่ไม่ซ้ำกันทั่วโลกสำหรับที่เก็บแต่ละแห่ง ตัวแปรนี้จะเป็นชื่อไดเรกทอรีที่มีที่เก็บอยู่
    โดยมีโครงสร้างดังนี้ (คำเตือน: รูปแบบชื่อ Canonical ไม่ใช่ API ที่ควรพึ่งพา และอาจมีการเปลี่ยนแปลงได้ทุกเมื่อ)

    • สำหรับที่เก็บโมดูล Bazel: module_name~version
      (ตัวอย่าง @bazel_skylib~1.0.3)
    • สำหรับที่เก็บส่วนขยายโมดูล: module_name~version~extension_name~repo_name
      (ตัวอย่าง @rules_cc~0.0.1~cc_configure~local_config_cc)
  • ชื่อที่เก็บที่ปรากฏ: ชื่อที่เก็บที่จะใช้ในไฟล์ BUILD และ .bzl ภายในที่เก็บ ทรัพยากร Dependency เดียวกันอาจมีชื่อ ที่เห็นได้ชัดในที่เก็บที่ต่างกัน
    มีการกำหนดไว้ดังนี้

    • สำหรับที่เก็บโมดูล Bazel ให้ใช้ module_name โดยค่าเริ่มต้นหรือชื่อที่ระบุโดยแอตทริบิวต์ repo_name ใน bazel_dep
    • สำหรับที่เก็บส่วนขยายโมดูล: แนะนำชื่อที่เก็บผ่าน use_repo

ที่เก็บทุกแห่งมีพจนานุกรมการแมปที่เก็บสำหรับทรัพยากร Dependency โดยตรง ซึ่งเป็นแมปจากชื่อที่เก็บที่ปรากฏไปจนถึงชื่อที่เก็บแบบ Canonical เราใช้การแมปที่เก็บเพื่อแก้ไขชื่อที่เก็บเมื่อสร้างป้ายกำกับ โปรดทราบว่าชื่อที่เก็บ Canonical จะไม่ขัดแย้งกัน และคุณจะค้นพบการใช้ชื่อที่เก็บที่ปรากฏได้โดยการแยกวิเคราะห์ไฟล์ MODULE.bazel ดังนั้นข้อขัดแย้งจึงตรวจพบและแก้ไขได้ง่ายโดยไม่ส่งผลกระทบต่อทรัพยากร Dependency อื่นๆ

ระดับความเข้มงวด

รูปแบบข้อมูลจำเพาะของทรัพยากร Dependency ใหม่ช่วยให้เราตรวจสอบได้อย่างเข้มงวดยิ่งขึ้น โดยเฉพาะอย่างยิ่ง ตอนนี้เราบังคับให้โมดูลใช้ได้เฉพาะที่เก็บที่สร้างจากทรัพยากร Dependency โดยตรงเท่านั้น วิธีนี้จะช่วยป้องกันไม่ให้มีการหยุดทำงานโดยไม่ตั้งใจหรือที่แก้ไขข้อบกพร่องได้ยาก เมื่อมีการเปลี่ยนแปลงในกราฟทรัพยากร Dependency แบบทรานซิทีฟ

ระบบติดตั้งใช้งานการเข้มงวดตามการแมปที่เก็บ โดยพื้นฐานแล้ว การแมปที่เก็บสำหรับแต่ละที่เก็บมีทรัพยากร Dependency โดยตรงทั้งหมดของที่เก็บอื่นจะไม่แสดง ระบบจะพิจารณาทรัพยากร Dependency ที่มองเห็นได้สำหรับที่เก็บแต่ละรายการดังนี้

  • ที่เก็บโมดูล Bazel จะดูที่เก็บทั้งหมดที่แนะนำในไฟล์ MODULE.bazel ได้ผ่านทาง bazel_dep และ use_repo
  • ที่เก็บของส่วนขยายโมดูลสามารถดูทรัพยากร Dependency ที่มองเห็นได้ทั้งหมดของโมดูลที่ให้ส่วนขยายดังกล่าว รวมถึงที่เก็บอื่นๆ ทั้งหมดที่สร้างขึ้นโดยส่วนขยายโมดูลเดียวกัน

รีจิสทรี

Bzlmod ค้นพบทรัพยากร Dependency ด้วยการขอข้อมูลจากรีจิสทรีของ Bazel รีจิสทรี Bazel เป็นเพียงฐานข้อมูลของโมดูล Bazel รีจิสทรีรูปแบบเดียวที่รองรับคือรีจิสทรีดัชนีซึ่งเป็นไดเรกทอรีในเครื่องหรือเซิร์ฟเวอร์ HTTP แบบคงที่ตามรูปแบบที่เฉพาะเจาะจง ในอนาคต เราวางแผนที่จะเพิ่มการรองรับรีจิสทรีโมดูลเดียวซึ่งเป็นเพียงที่เก็บ Git ที่มีแหล่งที่มาและประวัติของโปรเจ็กต์

รีจิสทรีดัชนี

รีจิสทรีดัชนีคือไดเรกทอรีในเครื่องหรือเซิร์ฟเวอร์ HTTP แบบคงที่ที่มีข้อมูลเกี่ยวกับรายการโมดูล ซึ่งรวมถึงหน้าแรก ผู้ดูแล ไฟล์ MODULE.bazel ของแต่ละเวอร์ชัน และวิธีดึงข้อมูลแหล่งที่มาของแต่ละเวอร์ชัน ที่สำคัญคือ ไม่จำเป็นต้องแสดงที่เก็บซอร์สด้วยตัวเอง

รีจิสทรีดัชนีต้องเป็นไปตามรูปแบบด้านล่าง

  • /bazel_registry.json: ไฟล์ JSON ที่มีข้อมูลเมตาสำหรับรีจิสทรี เช่น
    • mirrors ระบุรายการมิเรอร์ที่จะใช้สำหรับที่เก็บถาวรของแหล่งที่มา
    • module_base_path ซึ่งระบุเส้นทางฐานสำหรับโมดูลที่มีประเภท local_repository ในไฟล์ source.json
  • /modules: ไดเรกทอรีที่มีไดเรกทอรีย่อยสำหรับแต่ละโมดูลในรีจิสทรีนี้
  • /modules/$MODULE: ไดเรกทอรีที่มีไดเรกทอรีย่อยสำหรับแต่ละเวอร์ชันของโมดูลนี้ รวมทั้งไฟล์ต่อไปนี้
    • metadata.json: ไฟล์ JSON ที่มีข้อมูลเกี่ยวกับโมดูล พร้อมด้วยช่องต่อไปนี้
      • homepage: URL หน้าแรกของโปรเจ็กต์
      • maintainers: รายการออบเจ็กต์ JSON โดยแต่ละรายการจะสอดคล้องกับข้อมูลผู้ดูแลของโมดูลในรีจิสทรี โปรดทราบว่าชื่อนี้ไม่จำเป็นต้องเหมือนกับผู้เขียนของโปรเจ็กต์
      • versions: รายการเวอร์ชันทั้งหมดของโมดูลนี้ที่จะอยู่ในรีจิสทรีนี้
      • yanked_versions: รายการเวอร์ชันที่แยกของโมดูลนี้ ปัจจุบันนี่ยังไม่มีการดำเนินการ แต่ในอนาคต เวอร์ชันที่แยกออกใหม่จะข้ามผ่านหรือทำให้เกิดข้อผิดพลาด
  • /modules/$MODULE/$VERSION: ไดเรกทอรีที่มีไฟล์ต่อไปนี้
    • MODULE.bazel: ไฟล์ MODULE.bazel ของโมดูลเวอร์ชันนี้
    • source.json: ไฟล์ JSON ที่มีข้อมูลเกี่ยวกับวิธีดึงข้อมูลแหล่งที่มาของโมดูลเวอร์ชันนี้
      • ประเภทเริ่มต้นคือ "เก็บถาวร" พร้อมด้วยช่องต่อไปนี้
        • url: URL ของที่เก็บถาวรต้นทาง
        • integrity: ผลรวมตรวจสอบ ความสมบูรณ์ของทรัพยากรย่อย ของที่เก็บถาวร
        • strip_prefix: คำนำหน้าไดเรกทอรีที่จะตัดออกเมื่อแยกที่เก็บถาวรต้นทาง
        • patches: รายการสตริง แต่ละชื่อเป็นไฟล์แพตช์ที่จะใช้กับที่เก็บถาวรที่แตกข้อมูล โดยไฟล์แพตช์จะอยู่ในไดเรกทอรี /modules/$MODULE/$VERSION/patches
        • patch_strip: เหมือนกับอาร์กิวเมนต์ --strip ของแพตช์ Unix
      • คุณเปลี่ยนประเภทเพื่อใช้เส้นทางภายในที่มีช่องต่อไปนี้ได้
        • type: local_path
        • path: เส้นทางภายในไปยังที่เก็บซึ่งมีวิธีคำนวณดังนี้
          • หากเส้นทางเป็นเส้นทางสัมบูรณ์ ระบบจะใช้ตามที่เป็นอยู่
          • หากเส้นทางเป็นเส้นทางแบบสัมพัทธ์และ module_base_path เป็นเส้นทางสัมบูรณ์ ระบบจะแก้ไขเส้นทางเป็น <module_base_path>/<path>
          • หากเส้นทางและ module_base_path เป็นเส้นทางสัมพัทธ์ทั้งคู่ ระบบจะแก้ไขเส้นทางเป็น <registry_path>/<module_base_path>/<path> รีจิสทรีต้องโฮสต์ในเครื่องและใช้โดย --registry=file://<registry_path> มิฉะนั้น Bazel จะแสดงข้อผิดพลาด
    • patches/: ไดเรกทอรีที่ไม่บังคับซึ่งมีไฟล์แพตช์ ใช้เมื่อ source.json มีประเภท "เก็บถาวร" เท่านั้น

สำนักทะเบียน Bazel Central

Bazel Central Registry (BCR) เป็นรีจิสทรีดัชนีที่อยู่ใน bcr.bazel.build เนื้อหาได้รับการสนับสนุนโดยที่เก็บ GitHub bazelbuild/bazel-central-registry

BCR ดูแลรักษาโดยชุมชน Bazel ผู้ร่วมให้ข้อมูลสามารถส่งคำขอพุลได้ โปรดดูกระบวนการและนโยบายของ Bazel Central Registry

นอกเหนือจากรูปแบบรีจิสทรีดัชนีปกติแล้ว BCR ยังต้องมีไฟล์ presubmit.yml สำหรับแต่ละเวอร์ชันโมดูล (/modules/$MODULE/$VERSION/presubmit.yml) ไฟล์นี้ระบุเป้าหมายการสร้างและทดสอบที่จำเป็นบางส่วนที่สามารถใช้เพื่อตรวจสอบความถูกต้องของเวอร์ชันโมดูลนี้ และใช้โดยไปป์ไลน์ CI ของ BCR เพื่อให้แน่ใจว่าจะทำงานร่วมกันระหว่างโมดูลใน BCR ได้

การเลือกรีจิสทรี

คุณสามารถใช้แฟล็ก Bazel --registry ที่ทำซ้ำได้เพื่อระบุรายการรีจิสทรีที่จะขอโมดูล ดังนั้นคุณจึงตั้งค่าโปรเจ็กต์ให้ดึงข้อมูลทรัพยากร Dependency จากบุคคลที่สามหรือรีจิสทรีภายในได้ รีจิสทรีเก่าๆ จะมีประสิทธิภาพกว่า เพื่อความสะดวก คุณสามารถใส่รายการ Flag --registry ในไฟล์ .bazelrc ของโปรเจ็กต์ได้

ส่วนขยายโมดูล

ส่วนขยายโมดูลช่วยให้คุณขยายระบบโมดูลได้โดยการอ่านข้อมูลอินพุตจากโมดูลต่างๆ ทั่วทั้งกราฟทรัพยากร Dependency ดำเนินการตรรกะที่จำเป็นเพื่อแก้ไขทรัพยากร Dependency และสุดท้ายสร้างที่เก็บด้วยการเรียกใช้กฎที่เก็บ ซึ่งมีฟังก์ชันการทำงานคล้ายกับมาโคร WORKSPACE ในปัจจุบัน แต่จะเหมาะสมกว่าสำหรับโมดูลและการขึ้นต่อกันแบบสับเปลี่ยน

ส่วนขยายโมดูลจะกำหนดไว้ในไฟล์ .bzl เช่นเดียวกับกฎที่เก็บหรือมาโคร WORKSPACE จะไม่มีการเรียกใช้โดยตรง แต่แต่ละโมดูลสามารถระบุข้อมูลที่เรียกว่าแท็กสำหรับส่วนขยายที่จะอ่าน จากนั้นเมื่อแก้ปัญหาเวอร์ชันโมดูลเสร็จแล้ว ส่วนขยายโมดูลจะทำงาน ส่วนขยายแต่ละรายการจะทำงานเพียงครั้งเดียวหลังจากการแปลงโมดูล (ยังเกิดขึ้นก่อนที่บิลด์จะเกิดขึ้นจริง) และอ่านแท็กทั้งหมดที่เป็นของส่วนขยายนั้นๆ ในกราฟทรัพยากร Dependency ทั้งหมด

          [ A 1.1                ]
          [   * maven.dep(X 2.1) ]
          [   * maven.pom(...)   ]
              /              \
   bazel_dep /                \ bazel_dep
            /                  \
[ B 1.2                ]     [ C 1.0                ]
[   * maven.dep(X 1.2) ]     [   * maven.dep(X 2.1) ]
[   * maven.dep(Y 1.3) ]     [   * cargo.dep(P 1.1) ]
            \                  /
   bazel_dep \                / bazel_dep
              \              /
          [ D 1.4                ]
          [   * maven.dep(Z 1.4) ]
          [   * cargo.dep(Q 1.1) ]

ในกราฟทรัพยากร Dependency ด้านบน A 1.1 และ B 1.2 เป็นต้น คือโมดูล Bazel คุณสามารถมองว่าแต่ละรายการเป็นไฟล์ MODULE.bazel ก็ได้ แต่ละโมดูลสามารถระบุบางแท็กสำหรับส่วนขยายโมดูล บางแท็กจะระบุสำหรับส่วนขยาย "Maven" และบางส่วนระบุสำหรับ "cargo" เมื่อกราฟทรัพยากร Dependency นี้เสร็จสมบูรณ์ (เช่น จริงๆ แล้ว B 1.2 มี bazel_dep ใน D 1.3 แต่ได้อัปเกรดเป็น D 1.4 เนื่องจาก C) ส่วนขยาย "maven" จะทำงานและอ่านแท็ก maven.* ทั้งหมดโดยใช้ข้อมูลในนั้นเพื่อตัดสินใจว่าจะสร้างที่เก็บใด เช่นเดียวกับส่วนขยาย "cargo"

การใช้ส่วนขยาย

ส่วนขยายจะโฮสต์อยู่ในโมดูล Bazel โดยตรง ดังนั้นหากต้องการใช้ส่วนขยายในโมดูล คุณต้องเพิ่ม bazel_dep บนโมดูลนั้นก่อน จากนั้นจึงเรียกใช้ฟังก์ชัน use_extension ในตัวเพื่อให้มีขอบเขต ลองดูตัวอย่างต่อไปนี้จากข้อมูลโค้ดจากไฟล์ MODULE.bazel เพื่อใช้ส่วนขยาย "maven" สมมติที่ระบุในโมดูล rules_jvm_external

bazel_dep(name = "rules_jvm_external", version = "1.0")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")

หลังจากนำส่วนขยายไปใช้กับขอบเขตแล้ว คุณสามารถใช้ไวยากรณ์จุดเพื่อระบุแท็กสำหรับส่วนขยายได้ โปรดทราบว่าแท็กต้องเป็นไปตามสคีมาที่กำหนดโดยคลาสแท็กที่เกี่ยวข้อง (ดูคำจำกัดความของส่วนขยายด้านล่าง) นี่คือตัวอย่างการระบุแท็ก maven.dep และ maven.pom บางส่วน

maven.dep(coord="org.junit:junit:3.0")
maven.dep(coord="com.google.guava:guava:1.2")
maven.pom(pom_xml="//:pom.xml")

หากส่วนขยายสร้างที่เก็บที่คุณต้องการใช้ในโมดูล ให้ใช้คำสั่ง use_repo เพื่อประกาศ การดำเนินการนี้เพื่อให้เป็นไปตามเงื่อนไขของการตรวจสอบที่เข้มงวดและหลีกเลี่ยงข้อขัดแย้งเกี่ยวกับชื่อของที่เก็บในพื้นที่

use_repo(
    maven,
    "org_junit_junit",
    guava="com_google_guava_guava",
)

ที่เก็บที่สร้างโดยส่วนขยายเป็นส่วนหนึ่งของ API ดังนั้นจากแท็กที่คุณระบุ คุณควรทราบว่าส่วนขยาย "maven" จะสร้างที่เก็บชื่อ "org_junit_junit" และที่เก็บชื่อว่า "com_google_guava_guava" ด้วย use_repo คุณสามารถเปลี่ยนชื่อแคมเปญให้อยู่ในขอบเขตของโมดูล เช่น "ฝรั่ง" ได้ที่นี่

คำจำกัดความของส่วนขยาย

การกำหนดส่วนขยายโมดูลจะคล้ายกับกฎที่เก็บ โดยใช้ฟังก์ชัน module_extension ทั้ง 2 อย่างมีฟังก์ชันการใช้งาน แต่แม้ว่ากฎที่เก็บจะมีแอตทริบิวต์หลายรายการ แต่ส่วนขยายโมดูลจะมี tag_class จำนวนหนึ่ง โดยแต่ละรายการจะมีแอตทริบิวต์จำนวนหนึ่ง คลาสแท็กจะกำหนดสคีมาสำหรับแท็กที่ส่วนขยายนี้ใช้ จากตัวอย่างส่วนขยาย "Maven" สมมติข้างต้น

# @rules_jvm_external//:extensions.bzl
maven_dep = tag_class(attrs = {"coord": attr.string()})
maven_pom = tag_class(attrs = {"pom_xml": attr.label()})
maven = module_extension(
    implementation=_maven_impl,
    tag_classes={"dep": maven_dep, "pom": maven_pom},
)

การประกาศเหล่านี้ระบุอย่างชัดเจนว่าคุณระบุแท็ก maven.dep และ maven.pom ได้โดยใช้สคีมาแอตทริบิวต์ที่ระบุไว้ข้างต้น

ฟังก์ชันการติดตั้งใช้งานคล้ายกับมาโคร WORKSPACE เว้นแต่ว่าจะได้รับออบเจ็กต์ module_ctx ซึ่งให้สิทธิ์เข้าถึงกราฟทรัพยากร Dependency และแท็กที่เกี่ยวข้องทั้งหมด จากนั้นฟังก์ชันการใช้งานควรเรียกใช้กฎที่เก็บเพื่อสร้างที่เก็บดังนี้

# @rules_jvm_external//:extensions.bzl
load("//:repo_rules.bzl", "maven_single_jar")
def _maven_impl(ctx):
  coords = []
  for mod in ctx.modules:
    coords += [dep.coord for dep in mod.tags.dep]
  output = ctx.execute(["coursier", "resolve", coords])  # hypothetical call
  repo_attrs = process_coursier(output)
  [maven_single_jar(**attrs) for attrs in repo_attrs]

ในตัวอย่างข้างต้น เราดูโมดูลทั้งหมดในกราฟทรัพยากร Dependency (ctx.modules) แต่ละโมดูลจะเป็นออบเจ็กต์ bazel_module ที่ช่อง tags จะแสดงแท็ก maven.* ทั้งหมดในโมดูลดังกล่าว จากนั้นเราเรียกใช้ยูทิลิตี CLI Coursier เพื่อติดต่อ Maven และดำเนินการแก้ปัญหา สุดท้าย เราใช้ผลลัพธ์การแก้ปัญหาเพื่อสร้างจำนวนที่เก็บโดยใช้กฎที่เก็บ maven_single_jar สมมติ