โมดูล Bazel

รายงานปัญหา ดูแหล่งที่มา

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

โมดูลต้องมีไฟล์ 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 ของทรัพยากร Dependency จากรีจิสทรีของ Bazel ซ้ำๆ จนกว่าจะพบกราฟทรัพยากร Dependency ทั้งหมด

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

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

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

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

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

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

เวอร์ชัน Yanked

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

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

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

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

ลบล้าง

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

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

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

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

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

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

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

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

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

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

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

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

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

ชื่อที่เก็บและ Dep ที่แน่นอน

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

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

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