โมดูล Bazel

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

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

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

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

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

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

  • SemVer กำหนดว่าส่วน "รุ่น" ของเวอร์ชันต้องประกอบด้วย 3 ส่วน: MAJOR.MINOR.PATCH ใน Bazel ข้อกำหนดนี้จะผ่อนปรนเพื่อให้ อนุญาตให้มีจำนวนกลุ่มเท่าใดก็ได้
  • ใน SemVer แต่ละส่วนในส่วน "รุ่น" ต้องเป็นตัวเลขเท่านั้น ใน 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 จะสร้างกระบวนการเลือกเวอร์ชันที่มีความเที่ยงตรงสูงและทำซ้ำได้

เวอร์ชันที่ถูกยกเลิก

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

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

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

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

ลบล้าง

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

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

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

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

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

แอตทริบิวต์เหล่านี้เป็นแอตทริบิวต์ที่ไม่บังคับทั้งหมด และสามารถนำมาใช้ร่วมกันได้

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

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

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

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

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

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

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

ชื่อที่เก็บและ Strict Deps

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

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

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