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