โมดูล Bazel

รายงานปัญหา ดูแหล่งที่มา

โมดูล Bazel คือโปรเจ็กต์ Bazel ที่มีได้หลายเวอร์ชัน โดยแต่ละเวอร์ชันจะเผยแพร่ข้อมูลเมตาเกี่ยวกับโมดูลอื่นๆ ที่ขึ้นอยู่กับโมดูล ซึ่งคล้ายกับแนวคิดที่คุ้นเคยในระบบการจัดการทรัพยากร Dependency อื่นๆ เช่น อาร์ติแฟกต์ Maven, แพ็กเกจ NPM, โมดูล Go หรือกล่องสินค้า

โมดูลต้องมีไฟล์ 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 แต่ละส่วนในส่วน "เผยแพร่" ต้องเป็นตัวเลขเท่านั้น ใน Bazel แอตทริบิวต์นี้จะเป็นแบบหลวมๆ เพื่อให้สามารถใส่ตัวอักษรได้ด้วย และความหมายการเปรียบเทียบจะตรงกับ "ตัวระบุ" ในส่วน "รุ่นทดลอง"
  • นอกจากนี้ จะไม่มีการบังคับใช้ความหมายของเวอร์ชันหลัก เวอร์ชันย่อย และเวอร์ชันแพตช์ที่เพิ่มขึ้น อย่างไรก็ตาม ดูรายละเอียดเกี่ยวกับวิธีที่เราแสดงถึงความเข้ากันได้แบบย้อนหลังได้ที่ระดับความเข้ากันได้

เวอร์ชัน SemVer ที่ถูกต้องคือเวอร์ชันโมดูล Bazel ที่ถูกต้อง นอกจากนี้ SemVer 2 เวอร์ชัน a และ b จะเปรียบเทียบ a < b ก็ต่อเมื่อมีการคงไว้ชั่วคราวเดียวกันเมื่อเปรียบเทียบกับเวอร์ชันโมดูล Bazel

การเลือกเวอร์ชัน

ลองพิจารณาปัญหาทรัพยากร Dependency ของ Diamond ซึ่งเป็นองค์ประกอบหลักในพื้นที่การจัดการทรัพยากร 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 จะสร้างกระบวนการเลือกเวอร์ชันที่มีความแม่นยำสูงและทำซ้ำได้

เวอร์ชัน Yank

รีจิสทรีสามารถประกาศว่าบางเวอร์ชันถูกถอน หากควรหลีกเลี่ยง (เช่น เนื่องด้วยช่องโหว่ด้านความปลอดภัย) Bazel แสดงข้อผิดพลาดเมื่อเลือกโมดูล เวอร์ชันที่ถูกดึงออก หากต้องการแก้ไขข้อผิดพลาดนี้ ให้อัปเกรดเป็นเวอร์ชันใหม่กว่าที่ไม่มีการ Y หรือใช้แฟล็ก --allow_yanked_versions เพื่ออนุญาตเวอร์ชัน Y อย่างชัดเจน

ระดับความเข้ากันได้

ใน Go การคาดคะเนของ MVS เกี่ยวกับความเข้ากันได้แบบย้อนหลังนั้นใช้งานได้ เนื่องจากระบบจะถือว่าโมดูลเวอร์ชันที่เข้ากันไม่ได้ย้อนหลังเป็นโมดูลที่แยกต่างหาก ในแง่ของ SemVer นั่นหมายความว่า A 1.x และ A 2.x ถือเป็นโมดูลที่แตกต่างกัน และอยู่ร่วมกันได้ในกราฟการอ้างอิงที่แก้ไขแล้ว ซึ่งในทางกลับกัน ด้วยการเข้ารหัสเวอร์ชันหลักในเส้นทางแพ็กเกจใน Go เพื่อให้ไม่มีความขัดแย้งเวลาคอมไพล์หรือเวลาในการลิงก์

อย่างไรก็ตาม Bazel ให้การรับประกันดังกล่าวไม่ได้ จึงต้องใช้หมายเลข "เวอร์ชันหลัก" เพื่อตรวจหาเวอร์ชันที่เข้ากันไม่ได้แบบย้อนหลัง ตัวเลขนี้เรียกว่าระดับความเข้ากันได้ และระบุโดยเวอร์ชันโมดูลแต่ละเวอร์ชันในคำสั่ง module() ข้อมูลนี้ช่วยให้ Bazel แสดงข้อผิดพลาดเมื่อตรวจพบว่าโมดูลเดียวกันที่มีระดับความเข้ากันได้แตกต่างกันแสดงอยู่ในกราฟทรัพยากร Dependency ที่แก้ไขแล้ว

ลบล้าง

ระบุการลบล้างในไฟล์ MODULE.bazel เพื่อเปลี่ยนลักษณะการทำงานของความละเอียดของโมดูล Bazel เฉพาะการลบล้างของโมดูลรูทเท่านั้นที่จะมีผล หากใช้โมดูลเป็นทรัพยากร Dependency การลบล้างของโมดูลจะไม่มีผล

การลบล้างแต่ละรายการจะได้รับการระบุสำหรับชื่อโมดูลหนึ่งๆ ซึ่งจะส่งผลต่อทุกเวอร์ชันในกราฟการอ้างอิง แม้ว่าการลบล้างของโมดูลรากจะมีผลเท่านั้น แต่การลบล้างอาจมีผลกับทรัพยากร Dependency ชั่วคราวที่โมดูลรูทไม่ได้ขึ้นอยู่โดยตรง

การลบล้างเวอร์ชันเดียว

single_version_override มีวัตถุประสงค์หลายอย่าง ดังนี้

  • คุณใช้แอตทริบิวต์ version เพื่อปักหมุดทรัพยากร Dependency ให้เป็นเวอร์ชันที่ต้องการได้ ไม่ว่าจะขอเวอร์ชันใดของทรัพยากร Dependency ในกราฟ Dependency ก็ตาม
  • เมื่อใช้แอตทริบิวต์ registry คุณจะบังคับให้ทรัพยากร Dependency นี้มาจากรีจิสทรีที่เฉพาะเจาะจง แทนการทำตามขั้นตอนการเลือกรีจิสทรีปกติได้
  • ด้วยแอตทริบิวต์ 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 เพื่อกำหนดที่เก็บโดยตรงโดยเรียกใช้กฎที่เก็บ ที่เก็บนี้จะปรากฏต่อโมดูลที่กำหนดเท่านั้น

การทำงานขั้นสูงจะติดตั้งใช้งานโดยใช้กลไกเดียวกันกับส่วนขยายโมดูล ซึ่งจะช่วยให้คุณกำหนด Repos ได้อย่างยืดหยุ่นมากขึ้น

ชื่อที่เก็บและองค์ประกอบที่เฉพาะเจาะจง

ชื่อที่แสดงของที่เก็บสำรองโมดูลไปยังทรัพยากร Dependency โดยตรงจะมีค่าเริ่มต้นเป็นชื่อโมดูล เว้นแต่ว่าแอตทริบิวต์ repo_name ของคำสั่ง bazel_dep จะระบุไว้เป็นอย่างอื่น โปรดทราบว่าวิธีนี้จะทําให้โมดูลค้นหาการขึ้นต่อกันโดยตรงได้เท่านั้น ซึ่งจะช่วยป้องกันไม่ให้เกิดการหยุดทำงานโดยไม่ตั้งใจเนื่องจากการเปลี่ยนแปลงในทรัพยากร Dependency แบบชั่วคราว

ชื่อ Canonical ของที่เก็บสำรองโมดูลจะเป็น module_name~version (เช่น bazel_skylib~1.0.3) หรือ module_name~ (เช่น bazel_features~) ขึ้นอยู่กับว่ามีโมดูลนี้หลายเวอร์ชันในกราฟการอ้างอิงทั้งหมดหรือไม่ (ดู multiple_version_override) โปรดทราบว่ารูปแบบชื่อ Canonical ไม่ใช่ API ที่คุณควรใช้และอาจมีการเปลี่ยนแปลงได้ทุกเมื่อ แทนที่จะฮาร์ดโค้ดชื่อ Canonical ให้ใช้วิธีที่รองรับเพื่อรับจาก Bazel โดยตรงดังนี้ * ในไฟล์ BUILD และ .bzl ให้ใช้ Label.repo_name ในอินสแตนซ์ Label ที่สร้างจากสตริงป้ายกำกับที่ระบุโดยชื่อที่ชัดเจนของที่เก็บ เช่น Label("@bazel_skylib").repo_name. * เมื่อค้นหา Runfile ให้ใช้ $(rlocationpath ...) หรือไลบรารี Runfile หนึ่งใน @bazel_tools//tools/{bash,cpp,java}/runfiles หรือสำหรับชุดกฎ rules_foo ใน @rules_foo//foo/runfiles * เมื่อโต้ตอบกับ Bazel จากเครื่องมือภายนอก เช่น IDE หรือเซิร์ฟเวอร์ภาษา ให้ใช้คำสั่ง bazel mod dump_repo_mapping เพื่อรับการแมปจากชื่อที่เห็นได้ชัดเป็นชื่อ Canonical สำหรับชุดที่เก็บที่ระบุ

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