โมดูล Bazel คือโปรเจ็กต์ Bazel ที่มีได้หลายเวอร์ชัน โดยแต่ละเวอร์ชันจะเผยแพร่ข้อมูลเมตาเกี่ยวกับโมดูลอื่นๆ ที่ใช้ร่วมกัน ซึ่งคล้ายกับแนวคิดที่คุ้นเคยในระบบการจัดการทรัพยากรอื่นๆ เช่น อาร์ติแฟกต์ Maven, แพ็กเกจ npm, โมดูล Go หรือ Crate ของ Cargo
โมดูลต้องมีไฟล์ MODULE.bazel
ที่รูทของรีโป ไฟล์นี้เป็นไฟล์ 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")
ดูรายการทั้งหมดของคําสั่งที่มีอยู่ในไฟล์MODULE.bazel
ในการแก้ไขข้อขัดข้องของโมดูล 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 เหมือนกันเท่านั้น
การเลือกเวอร์ชัน
ลองพิจารณาปัญหาเกี่ยวกับ Diamond 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 จะสร้างกระบวนการเลือกเวอร์ชันที่มีความแม่นยำสูงและทำซ้ำได้
เวอร์ชันแย้ง
รีจิสทรีสามารถประกาศว่าเวอร์ชันหนึ่งๆ ถูกยกเลิกได้หากควรหลีกเลี่ยงเวอร์ชันนั้น (เช่น เพราะมีช่องโหว่ด้านความปลอดภัย) Bazel แสดงข้อผิดพลาด
เมื่อเลือกโมดูลที่แยกออกมา หากต้องการแก้ไขข้อผิดพลาดนี้ ให้อัปเกรดเป็นเวอร์ชันใหม่ที่ไม่ใช่เวอร์ชันที่ถูกคัดออก หรือใช้แฟล็ก --allow_yanked_versions
เพื่ออนุญาตเวอร์ชันที่ไม่ดีอย่างชัดเจน
ระดับความเข้ากันได้
ใน Go สมมติฐานของ MVS เกี่ยวกับการทำงานร่วมกันแบบย้อนหลังจะใช้งานได้เนื่องจากระบบจะถือว่าโมดูลเวอร์ชันที่เข้ากันไม่ได้แบบย้อนหลังเป็นโมดูลแยกต่างหาก ในแง่ของ SemVer หมายความว่า A 1.x
และ A 2.x
จะถือว่าเป็นโมดูลที่แตกต่างกัน และอยู่ร่วมกันได้ในกราฟการพึ่งพาที่แก้ไขแล้ว ซึ่งทำได้โดยการเข้ารหัสเวอร์ชันหลักในเส้นทางแพ็กเกจใน Go เพื่อให้ไม่มีข้อขัดแย้งเกิดขึ้นในเวลาคอมไพล์หรือเวลาลิงก์
อย่างไรก็ตาม Bazel ให้การรับประกันดังกล่าวไม่ได้ จึงต้องใช้หมายเลข "เวอร์ชันหลัก" เพื่อตรวจหาเวอร์ชันที่ใช้ร่วมกันไม่ได้ย้อนหลัง ตัวเลขนี้เรียกว่าระดับความเข้ากันได้ และแต่ละเวอร์ชันของโมดูลจะระบุไว้ในคำสั่ง module()
ข้อมูลนี้ช่วยให้ Bazel แสดงข้อผิดพลาดได้เมื่อตรวจพบว่ามีโมดูลเวอร์ชันเดียวกันที่มีระดับความเข้ากันได้ต่างกันอยู่ในกราฟความเกี่ยวข้องที่แก้ไขแล้ว
ลบล้าง
ระบุการลบล้างในไฟล์ 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 รองรับการลบล้างที่ไม่ใช่รีจิสทรีต่อไปนี้
กำหนดที่เก็บที่ไม่ได้แสดงถึงโมดูล Bazel
bazel_dep
ช่วยให้คุณกำหนดที่เก็บซึ่งแสดงโมดูล Bazel อื่นๆ ได้
บางครั้งจำเป็นต้องกำหนดที่เก็บซึ่งไม่ได้แสดงโมดูล Bazel เช่น ที่เก็บที่มีไฟล์ JSON ธรรมดาที่จะอ่านเป็นข้อมูล
ในกรณีนี้ คุณสามารถใช้use_repo_rule
คําสั่งเพื่อกําหนดพื้นที่เก็บข้อมูลโดยตรงด้วยการเรียกใช้กฎพื้นที่เก็บข้อมูล เฉพาะโมดูลที่กําหนดค่าไว้เท่านั้นที่จะมองเห็นที่เก็บข้อมูลนี้
ขั้นสูง วิธีนี้ใช้กลไกเดียวกันกับส่วนขยายโมดูล ซึ่งช่วยให้คุณกำหนดข้อความได้อย่างยืดหยุ่นมากขึ้น
ชื่อที่เก็บและข้อกำหนดที่เข้มงวด
ชื่อที่ปรากฏของรีโปที่สำรองข้อมูลโมดูลให้กับโมดูลที่ขึ้นต่อกันโดยตรงจะเป็นชื่อโมดูลโดยค่าเริ่มต้น เว้นแต่แอตทริบิวต์ repo_name
ของคำสั่ง bazel_dep
จะระบุไว้เป็นอย่างอื่น โปรดทราบว่าหมายความว่าโมดูลจะค้นหาได้เฉพาะข้อกําหนดโดยตรงเท่านั้น ซึ่งจะช่วยป้องกันความเสียหายโดยไม่ตั้งใจที่เกิดจากการเปลี่ยนแปลงในข้อกำหนดเบื้องต้นแบบทรานซิทีฟ
ชื่อที่เป็นค่ากำหนดของรีโปที่รองรับโมดูลคือ module_name+version
(เช่น bazel_skylib+1.0.3
) หรือ module_name+
(เช่น bazel_features+
) ทั้งนี้ขึ้นอยู่กับว่าโมดูลในกราฟความเกี่ยวข้องทั้งหมดมีเวอร์ชันหลายเวอร์ชันหรือไม่ (ดู multiple_version_override
)
โปรดทราบว่ารูปแบบชื่อที่เป็นค่ากำหนดไม่ใช่ API ที่คุณควรใช้ และอาจมีการเปลี่ยนแปลงได้ทุกเมื่อ แทนที่จะฮาร์ดโค้ดชื่อ Canonical
ให้ใช้วิธีที่รองรับเพื่อรับชื่อจาก Bazel โดยตรง
- ในไฟล์ BUILD และ
.bzl
ให้ใช้Label.repo_name
ในอินสแตนซ์Label
ที่สร้างขึ้นจากสตริงป้ายกำกับซึ่งได้จากชื่อที่ปรากฏของรีโป เช่นLabel("@bazel_skylib").repo_name
- เมื่อค้นหาไฟล์รันไทม์ ให้ใช้
$(rlocationpath ...)
หรือไลบรารีไฟล์รันไทม์รายการใดรายการหนึ่งใน@bazel_tools//tools/{bash,cpp,java}/runfiles
หรือสำหรับชุดกฎrules_foo
ให้ใช้@rules_foo//foo/runfiles
- เมื่อโต้ตอบกับ Bazel จากเครื่องมือภายนอก เช่น IDE หรือเซิร์ฟเวอร์ภาษา ให้ใช้คำสั่ง
bazel mod dump_repo_mapping
เพื่อรับการแมปจากชื่อที่ปรากฏเป็นชื่อ Canonical สำหรับชุดที่เก็บข้อมูลหนึ่งๆ
ส่วนขยายของโมดูลยังนํารีพอสิทเพิ่มเติมมาไว้ในขอบเขตที่มองเห็นได้ของโมดูลได้ด้วย