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

รายงานปัญหา ดูซอร์สโค้ด รุ่น Nightly · 8.0 7.4 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

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

โมดูล Bazel

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

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

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

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) คุณไม่จำเป็นต้องระบุทรัพยากร Dependency แบบสื่อกลางในไฟล์ MODULE.bazel ต่างจากไฟล์ WORKSPACE แต่ควรระบุเฉพาะทรัพยากร Dependency โดยตรงเท่านั้น ระบบจะประมวลผลไฟล์ MODULE.bazel ของทรัพยากร Dependency เพื่อค้นหาทรัพยากร Dependency แบบสื่อกลางโดยอัตโนมัติ

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

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

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

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

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

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

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

การแก้ไขเวอร์ชัน

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

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

ชื่อที่เก็บ

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

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

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

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

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

ไลบรารีที่ต้องติดตั้ง

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

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

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

รีจิสทรี

Bzlmod จะค้นหาข้อมูลพึ่งพาโดยขอข้อมูลจากรีจิสทรีของ 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: Checksum ของไฟล์เก็บถาวรสำหรับความสมบูรณ์ของเนื้อหาย่อย
        • strip_prefix: คำนำหน้าไดเรกทอรีที่จะตัดออกเมื่อแตกไฟล์เก็บถาวรต้นทาง
        • patches: รายการสตริง โดยแต่ละรายการจะเป็นชื่อไฟล์แพตช์ที่จะใช้กับไฟล์ที่เก็บถาวรที่แตกไฟล์ออกมา ไฟล์แพตช์จะอยู่ภายใต้ไดเรกทอรี /modules/$MODULE/$VERSION/patches
        • patch_strip: เหมือนกับอาร์กิวเมนต์ --strip ของแพตช์ Unix
      • คุณเปลี่ยนประเภทเป็นการใช้เส้นทางในเครื่องได้โดยใช้ช่องต่อไปนี้
        • type: local_path
        • path: เส้นทางในเครื่องไปยังที่เก็บซึ่งคํานวณดังนี้
          • หากเป็นเส้นทางแบบสัมบูรณ์ ระบบจะใช้เส้นทางนั้นตามที่เป็นอยู่
          • หาก path เป็นเส้นทางแบบสัมพัทธ์และ module_base_path เป็นเส้นทางแบบสัมบูรณ์ ระบบจะเปลี่ยนเส้นทาง path เป็น <module_base_path>/<path>
          • หากทั้ง path และ module_base_path เป็นเส้นทางแบบสัมพัทธ์ ระบบจะแปลง path เป็น <registry_path>/<module_base_path>/<path> โดยต้องโฮสต์รีจิสทรีในเครื่องและ --registry=file://<registry_path> เป็นผู้ใช้งาน ไม่เช่นนั้น Bazel จะแสดงข้อผิดพลาด
    • patches/: ไดเรกทอรีที่ไม่บังคับซึ่งมีไฟล์แพตช์ จะใช้ก็ต่อเมื่อ source.json มีประเภทเป็น "เก็บ"

รีจิสทรีส่วนกลางของ Bazel

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) ไฟล์นี้จะระบุเป้าหมายการสร้างและทดสอบที่สำคัญ 2-3 รายการที่สามารถใช้ตรวจสอบความถูกต้องของเวอร์ชันโมดูลนี้ และถูกใช้โดยไปป์ไลน์ CI ของ BCR เพื่อให้มั่นใจว่าโมดูลต่างๆ ใน BCR จะทำงานร่วมกันได้

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

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

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

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

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

          [ 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) ]

ในตัวอย่างกราฟความเกี่ยวข้องด้านบน A 1.1 และ B 1.2 ฯลฯ คือโมดูล Bazel ซึ่งคุณอาจมองแต่ละโมดูลเป็นไฟล์ MODULE.bazel โมดูลแต่ละรายการสามารถระบุแท็กบางอย่างสำหรับส่วนขยายของโมดูลได้ ในที่นี้มีการระบุแท็กบางส่วนสำหรับส่วนขยาย "maven" และบางส่วนสำหรับ "cargo" เมื่อกราฟนี้เสร็จสมบูรณ์ (เช่น 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 เพื่อประกาศที่เก็บข้อมูล การดำเนินการนี้เป็นไปตามเงื่อนไข deps ที่เข้มงวดและหลีกเลี่ยงความขัดแย้งของชื่อที่เก็บข้อมูลในเครื่อง

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

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

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

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

# @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]

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