โมดูล 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 แบบสกรรม
ส่วนขยายโมดูลยังแนะนำที่เก็บเพิ่มเติมลงในขอบเขตที่มองเห็นได้ของโมดูลได้อีกด้วย