โมดูล Bazel

รายงานปัญหา ดูแหล่งที่มา Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

โมดูล 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 เพื่อรับการแมปจากชื่อที่ปรากฏไปยังชื่อที่แน่นอนสำหรับชุดที่เก็บที่กำหนด

ส่วนขยายโมดูลยังสามารถนำที่เก็บข้อมูลเพิ่มเติม มาไว้ในขอบเขตที่มองเห็นได้ของโมดูลด้วย