โมดูล Bazel

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

โมดูลต้องมีไฟล์ MODULE.bazel ที่รูทของรีโป (ข้างไฟล์ WORKSPACE) ไฟล์นี้เป็นไฟล์ Manifest ของโมดูล ซึ่งจะประกาศชื่อ เวอร์ชัน รายการของ 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")

ในการแก้ไขข้อขัดข้องของโมดูล Bazel จะเริ่มด้วยการอ่านไฟล์ MODULE.bazel ของโมดูลรูท จากนั้นจะขอไฟล์ MODULE.bazel ของข้อกำหนดต่างๆ จากรีจิสทรี Bazel ซ้ำๆ จนกว่าจะพบกราฟข้อกำหนดทั้งหมด

จากนั้น Bazel จะเลือกแต่ละโมดูล 1 เวอร์ชันเพื่อนำมาใช้โดยค่าเริ่มต้น Bazel แสดงแต่ละโมดูลด้วยที่เก็บ และปรึกษาสำนักทะเบียนอีกครั้งเพื่อดูวิธีกำหนดที่เก็บแต่ละห้อง

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

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

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

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

เวอร์ชัน SemVer ที่ถูกต้องคือเวอร์ชันโมดูล Bazel ที่ถูกต้อง นอกจากนี้ เวอร์ชัน SemVer 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 จะสร้างกระบวนการเลือกเวอร์ชันที่มีความแม่นยำสูงและทำซ้ำได้

เวอร์ชันที่ดึงออก

รีจิสทรีสามารถประกาศว่าเวอร์ชันหนึ่งๆ ถูกยกเลิกได้หากควรหลีกเลี่ยงเวอร์ชันนั้น (เช่น เพราะมีช่องโหว่ด้านความปลอดภัย) Bazel แสดงข้อผิดพลาด เมื่อเลือกโมดูลที่แยกออกมา หากต้องการแก้ไขข้อผิดพลาดนี้ ให้อัปเกรดเป็นเวอร์ชันใหม่ที่ไม่ใช่เวอร์ชันที่ถูกคัดออก หรือใช้แฟล็ก --allow_yanked_versions เพื่ออนุญาตเวอร์ชันที่ไม่ดีอย่างชัดเจน

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

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

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

ลบล้าง

ระบุการลบล้างในไฟล์ MODULE.bazel เพื่อเปลี่ยนลักษณะการแก้ไขโมดูลของ Bazel เฉพาะการลบล้างของโมดูลรูทเท่านั้นที่มีผล หากใช้โมดูลเป็นข้อกําหนด ระบบจะไม่สนใจการลบล้างของโมดูลนั้น

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

การลบล้างเวอร์ชันเดียว

single_version_override มีวัตถุประสงค์หลายอย่าง ดังนี้

  • แอตทริบิวต์ version ช่วยให้คุณปักหมุดทรัพยากร Dependency กับเวอร์ชันที่เจาะจงได้ ไม่ว่าจะมีการขอทรัพยากร Dependency เวอร์ชันใดในกราฟทรัพยากร Dependency ก็ตาม
  • เมื่อใช้แอตทริบิวต์ registry คุณสามารถบังคับให้ทรัพยากรนี้มาจากรีจิสทรีที่เฉพาะเจาะจงแทนที่จะทำตามกระบวนการการเลือกรีจิสทรีตามปกติ
  • เมื่อใช้แอตทริบิวต์ patch* คุณจะระบุชุดแพตช์ที่จะใช้กับโมดูลที่ดาวน์โหลดได้

แอตทริบิวต์เหล่านี้ไม่บังคับและสามารถผสมผสานกัน

การลบล้างหลายเวอร์ชัน

คุณสามารถระบุ multiple_version_override เพื่ออนุญาตให้โมดูลเดียวกันหลายเวอร์ชันอยู่ร่วมกันได้ในกราฟความสัมพันธ์ที่แก้ไขแล้ว

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

ตัวอย่างเช่น หากมีเวอร์ชัน 1.1, 1.3, 1.5, 1.7 และ 2.0 ในกราฟความเกี่ยวข้องก่อนการแก้ไข และเวอร์ชันหลักคือระดับความเข้ากันได้ ให้ทำดังนี้

  • การลบล้างหลายเวอร์ชันที่อนุญาต 1.3, 1.7 และ 2.0 จะส่งผลให้1.1ได้รับการอัปเกรดเป็น 1.3, 1.5 ได้รับการอัปเกรดเป็น 1.7 และเวอร์ชันอื่นๆ ยังคงเหมือนเดิม
  • การลบล้างหลายเวอร์ชันที่อนุญาต 1.5 และ 2.0 จะทำให้เกิดข้อผิดพลาด เนื่องจาก 1.7 ไม่มีเวอร์ชันที่สูงกว่าในระดับความเข้ากันได้เดียวกันที่จะอัปเกรด
  • การลบล้างหลายเวอร์ชันที่อนุญาต 1.9 และ 2.0 จะทำให้เกิดข้อผิดพลาด เนื่องจาก 1.9 ไม่อยู่ในกราฟความเกี่ยวข้องก่อนการแก้ไข

นอกจากนี้ ผู้ใช้ยังลบล้างรีจิสทรีโดยใช้แอตทริบิวต์ registry ได้ ซึ่งคล้ายกับการลบล้างเวอร์ชันเดียว

การลบล้างที่ไม่ใช่รีจิสทรี

การลบล้างที่ไม่ใช่รีจิสทรีจะนำโมดูลออกจากการแปลงเวอร์ชันโดยสมบูรณ์ Bazel จะไม่ขอไฟล์ MODULE.bazel เหล่านี้จากรีจิสทรี แต่จะขอจากรีโปโดยตรง

Bazel รองรับการลบล้างที่ไม่ใช่รีจิสทรีต่อไปนี้

ชื่อที่เก็บและหน่วยงานที่เข้มงวด

ชื่อ Canonical ของที่เก็บที่รองรับโมดูลคือ module_name~version (เช่น bazel_skylib~1.0.3) สำหรับโมดูลที่มีการลบล้างที่ไม่ใช่รีจิสทรี ให้แทนที่ส่วน version ด้วยสตริง override โปรดทราบว่ารูปแบบชื่อที่เป็นค่ากำหนดไม่ใช่ API ที่ควรใช้และอาจมีการเปลี่ยนแปลงได้ทุกเมื่อ

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

ส่วนขยายของโมดูลยังนํารีพอสิทเพิ่มเติมมาไว้ในขอบเขตที่มองเห็นได้ของโมดูลได้ด้วย