โมดูล Bazel คือโปรเจ็กต์ Bazel ที่มีได้หลายเวอร์ชัน โดยแต่ละเวอร์ชันจะเผยแพร่ข้อมูลเมตาเกี่ยวกับโมดูลอื่นๆ ที่ขึ้นอยู่กับโมดูลนั้น ซึ่งคล้ายกับแนวคิดที่คุ้นเคยในระบบการจัดการทรัพยากร Dependency อื่นๆ เช่น Artifact ของ Maven, Package ของ npm, Module ของ Go หรือ Crate ของ Cargo
โมดูลต้องมีไฟล์ MODULE.bazel
ที่รูทของ repo (ข้างไฟล์ 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")
ดูรายการทั้งหมดของคําสั่งที่มีในไฟล์ MODULE.bazel
ในการดำเนินการแก้ไขโมดูล Bazel จะเริ่มด้วยการอ่านไฟล์ MODULE.bazel
ของโมดูลรูท จากนั้นจะขอไฟล์ MODULE.bazel
ของการขึ้นต่อกันจากรีจิสทรี Bazel ซ้ำๆ จนกว่าจะค้นพบกราฟการขึ้นต่อกันทั้งหมด
โดยค่าเริ่มต้น Bazel จะเลือกโมดูลแต่ละเวอร์ชัน เพื่อใช้ Bazel จะแสดงแต่ละโมดูลด้วย repo และจะปรึกษา Registry อีกครั้งเพื่อดูวิธีกำหนดแต่ละ repo
รูปแบบเวอร์ชัน
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 ดังนี้
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()
Directive เมื่อมีข้อมูลนี้ Bazel จะแสดงข้อผิดพลาดเมื่อตรวจพบว่ามีโมดูลเดียวกันหลายเวอร์ชันที่มีระดับความเข้ากันได้ต่างกันอยู่ในกราฟการอ้างอิงที่แก้ไขแล้ว
ลบล้าง
ระบุการลบล้างในไฟล์ MODULE.bazel
เพื่อเปลี่ยนลักษณะการทำงานของการแก้ปัญหาโมดูล Bazel เฉพาะการลบล้างของโมดูลรูทเท่านั้นที่จะมีผล หากใช้โมดูลเป็นทรัพยากร Dependency ระบบจะไม่สนใจการลบล้างของโมดูลนั้น
การลบล้างแต่ละรายการจะระบุชื่อโมดูลหนึ่งๆ ซึ่งส่งผลต่อเวอร์ชันทั้งหมดของโมดูลนั้นในกราฟการอ้างอิง แม้ว่าการลบล้างของโมดูลรูทเท่านั้นที่จะมีผล แต่การลบล้างดังกล่าวอาจมีไว้สำหรับทรัพยากร Dependency แบบทรานซิทีฟที่โมดูลรูทไม่ได้ขึ้นอยู่กับโดยตรง
การลบล้างเวอร์ชันเดียว
single_version_override
มีวัตถุประสงค์หลายประการ ดังนี้
- แอตทริบิวต์
version
ช่วยให้คุณปักหมุดทรัพยากร 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
ช่วยให้คุณกำหนด repo ที่แสดงโมดูล Bazel อื่นๆ ได้
บางครั้งอาจจำเป็นต้องกำหนดที่เก็บที่ไม่แสดงโมดูล Bazel
เช่น ที่เก็บที่มีไฟล์ JSON ธรรมดาที่จะอ่านเป็นข้อมูล
ในกรณีนี้ คุณสามารถใช้use_repo_rule
คำสั่งเพื่อกำหนดรีโปโดยตรง
ด้วยการเรียกใช้กฎรีโป มีเพียงโมดูลที่กำหนดไว้เท่านั้นที่จะมองเห็นที่เก็บนี้
เบื้องหลังแล้วฟีเจอร์นี้ใช้กลไกเดียวกับส่วนขยาย โมดูล ซึ่งช่วยให้คุณกำหนดที่เก็บได้ยืดหยุ่นมากขึ้น
ชื่อที่เก็บและ Strict Deps
ชื่อที่ชัดเจนของที่เก็บที่สนับสนุนโมดูลไปยังโมดูลที่ขึ้นต่อกันโดยตรงจะตั้งค่าเริ่มต้นเป็นชื่อโมดูล เว้นแต่แอตทริบิวต์ repo_name
ของคำสั่ง bazel_dep
จะระบุไว้เป็นอย่างอื่น โปรดทราบว่าการดำเนินการนี้หมายความว่าโมดูลจะค้นหาได้เฉพาะการอ้างอิงโดยตรง
เท่านั้น ซึ่งจะช่วยป้องกันไม่ให้เกิดการหยุดทำงานโดยไม่ตั้งใจเนื่องจากการเปลี่ยนแปลงใน
การอ้างอิงแบบทรานซิทีฟ
ชื่อ Canonical ของที่เก็บข้อมูลที่สนับสนุนโมดูล
คือ module_name~version
(เช่น bazel_skylib~1.0.3
) หรือ module_name~
(เช่น bazel_features~
) ขึ้นอยู่กับว่ามีโมดูลหลายเวอร์ชันในกราฟการขึ้นต่อกันทั้งหมดหรือไม่ (ดู multiple_version_override
)
โปรดทราบว่ารูปแบบชื่อ Canonical ไม่ใช่ API ที่คุณควรใช้ และอาจมีการเปลี่ยนแปลงได้ทุกเมื่อ แทนที่จะเขียนโค้ดชื่อที่แน่นอน
ให้ใช้วิธีที่รองรับเพื่อรับชื่อดังกล่าวจาก 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
เพื่อรับการแมปจากชื่อที่ปรากฏไปยังชื่อที่แน่นอนสำหรับชุดที่เก็บที่กำหนด
ส่วนขยายโมดูลยังสามารถนำที่เก็บข้อมูลเพิ่มเติม มาไว้ในขอบเขตที่มองเห็นได้ของโมดูลด้วย