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

วันที่ รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

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

โมดูล Bazel

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

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

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

MODULE.bazel

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

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 เพื่อระบุโดยตรง ทรัพยากร Dependency ในโมดูล Bazel อื่นๆ
  • การลบล้าง ซึ่งใช้ได้เฉพาะโดยโมดูลรูทเท่านั้น (กล่าวคือ ไม่ใช่โดย ซึ่งใช้เป็นทรัพยากร Dependency ได้) เพื่อปรับแต่งลักษณะการทำงานของ การขึ้นต่อกันโดยตรงหรือแบบสับเปลี่ยนบางอย่าง:
  • คำสั่งที่เกี่ยวข้องกับส่วนขยายโมดูล

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

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

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

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

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

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

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

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

ชื่อที่เก็บ

ทรัพยากร Dependency ภายนอกทุกรายการใน Bazel จะมีชื่อที่เก็บ บางครั้ง ก็ ทรัพยากร Dependency อาจใช้ผ่านชื่อที่เก็บที่ต่างกันได้ (ตัวอย่างเช่น ทั้ง 2 อย่าง ค่าเฉลี่ย @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 registries รีจิสทรี Bazel เป็นเพียงฐานข้อมูลของโมดูล Bazel มีเพียง รูปแบบรีจิสทรีที่รองรับคือรีจิสทรีดัชนี ซึ่งเป็น ไดเรกทอรีภายในหรือเซิร์ฟเวอร์ HTTP แบบคงที่ตามรูปแบบที่เฉพาะเจาะจง ใน เราวางแผนที่จะเพิ่มการรองรับรีจิสทรีโมดูลเดียว ซึ่ง git repos ที่มีแหล่งที่มาและประวัติของโปรเจ็กต์

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

รีจิสทรีดัชนีคือไดเรกทอรีภายในหรือเซิร์ฟเวอร์ 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: ความสมบูรณ์ของทรัพยากรย่อย checksum ของที่เก็บถาวร
        • 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.* โดยใช้ข้อมูลในนั้นเพื่อตัดสินใจว่าจะสร้างที่เก็บใด เช่นเดียวกันกับ "cargo" ส่วนขยาย

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

ส่วนขยายจะโฮสต์อยู่ในโมดูล Bazel เอง ดังนั้นหากต้องการใช้ส่วนขยายใน คุณจะต้องเพิ่ม bazel_dep ลงในโมดูลนั้นก่อน จากนั้นจึงเรียกใช้ use_extension ในตัว มาไว้ในขอบเขต ลองดูตัวอย่างต่อไปนี้ ไฟล์ MODULE.bazel เพื่อใช้ "ผู้เชี่ยวชาญ" สมมติ ที่กำหนดไว้ใน โมดูล 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 สำหรับประกาศ ให้พวกเขา เพื่อให้เป็นไปตามเงื่อนไข Dependency ที่เข้มงวดและหลีกเลี่ยงการใช้ชื่อที่เก็บในพื้นที่ ความขัดแย้ง

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

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

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

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

# @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 สมมติ กฎที่เก็บ