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