Bzlmod คือชื่อรหัสของระบบทรัพยากร Dependency ภายนอกใหม่ ที่เปิดตัวใน Bazel 5.0 ซึ่งเปิดตัวมาเพื่อแก้ปัญหาหลายประการของระบบเก่าที่แก้ไขแบบค่อยๆ เป็นค่อยๆ ไปได้ยาก โปรดดูรายละเอียดเพิ่มเติมในส่วนคำอธิบายปัญหาของเอกสารการออกแบบเดิม
ใน Bazel 5.0 ระบบจะไม่ได้เปิด Bzlmod ไว้โดยค่าเริ่มต้น คุณต้องระบุแฟล็ก --experimental_enable_bzlmod เพื่อให้การตั้งค่าต่อไปนี้มีผล ฟีเจอร์นี้ยังอยู่ในขั้น ทดลอง ในปัจจุบัน
API และลักษณะการทำงานอาจเปลี่ยนแปลงไปจนกว่าจะเปิดตัวฟีเจอร์อย่างเป็นทางการ
หากต้องการย้ายข้อมูลโปรเจ็กต์ไปยัง Bzlmod ให้ทำตามคำแนะนำในการย้ายข้อมูล Bzlmod นอกจากนี้ คุณยังดูตัวอย่างการใช้งาน Bzlmod ได้ในที่เก็บตัวอย่าง
โมดูล Bazel
ระบบทรัพยากร Dependency ภายนอกเดิมที่อิงตาม WORKSPACE จะเน้นที่ ที่เก็บข้อมูล (หรือ repo) ซึ่งสร้างขึ้นผ่าน กฎของที่เก็บข้อมูล (หรือ กฎของ repo)
แม้ว่า repo จะยังคงเป็นแนวคิดที่สำคัญในระบบใหม่ แต่ โมดูล คือหน่วยหลักของทรัพยากร Dependency
โมดูล เป็นโปรเจ็กต์ Bazel ที่มีหลายเวอร์ชัน โดยแต่ละเวอร์ชันจะเผยแพร่ข้อมูลเมตาเกี่ยวกับโมดูลอื่นๆ ที่โมดูลนั้นขึ้นอยู่ด้วย ซึ่งคล้ายกับแนวคิดที่คุ้นเคยในระบบการจัดการทรัพยากร Dependency อื่นๆ เช่น _อาร์ติแฟกต์_ ของ Maven, _แพ็กเกจ_ ของ npm, _เครต_ ของ Cargo, _โมดูล_ ของ Go เป็นต้น
โมดูลจะระบุทรัพยากร Dependency โดยใช้คู่ name และ version แทน URL ที่เฉพาะเจาะจงใน WORKSPACE จากนั้นระบบจะค้นหาทรัพยากร Dependency ใน
รีจิสทรี Bazel ซึ่งเป็น
รีจิสทรีกลางของ Bazel โดยค่าเริ่มต้น ในพื้นที่ทำงาน ระบบจะเปลี่ยนโมดูลแต่ละรายการให้เป็น repo
MODULE.bazel
โมดูลทุกเวอร์ชันจะมีไฟล์ MODULE.bazel ที่ประกาศทรัพยากร 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 ควรอยู่ในไดเรกทอรีรูทของพื้นที่ทำงาน (ข้างไฟล์ WORKSPACE) คุณไม่จำเป็นต้องระบุทรัพยากร Dependency แบบทรานซิทีฟ เหมือนกับไฟล์ WORKSPACE แต่ควรรระบุเฉพาะทรัพยากร Dependency โดยตรง และระบบจะประมวลผลไฟล์ MODULE.bazel ของทรัพยากร Dependency เพื่อค้นหาทรัพยากร Dependency แบบทรานซิทีฟโดยอัตโนมัติ
ไฟล์ MODULE.bazel คล้ายกับไฟล์ BUILD เนื่องจากไม่รองรับการควบคุมโฟลว์ทุกรูปแบบ และยังไม่อนุญาตให้ใช้คำสั่ง load ด้วย คำสั่งที่ไฟล์ MODULE.bazel รองรับมีดังนี้
moduleเพื่อระบุข้อมูลเมตาเกี่ยวกับโมดูลปัจจุบัน รวมถึงชื่อ เวอร์ชัน และอื่นๆbazel_depเพื่อระบุทรัพยากร Dependency โดยตรงในโมดูล Bazel อื่นๆ- การลบล้าง ซึ่งใช้ได้เฉพาะโมดูลรูท (นั่นคือ โมดูลที่ไม่ได้ใช้เป็นทรัพยากร Dependency) เพื่อปรับแต่งลักษณะการทำงานของทรัพยากร Dependency โดยตรงหรือแบบทรานซิทีฟบางอย่าง ได้แก่
- คำสั่งที่เกี่ยวข้องกับส่วนขยายโมดูล:
รูปแบบเวอร์ชัน
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 แบบไดมอนด์เป็นปัญหาหลักในพื้นที่การจัดการทรัพยากร Dependency ที่มีการกำหนดเวอร์ชัน สมมติว่าคุณมีกราฟทรัพยากร Dependency ต่อไปนี้
A 1.0
/ \
B 1.0 C 1.1
| |
D 1.0 D 1.1
ควรใช้ D เวอร์ชันใด Bzlmod ใช้ การเลือกเวอร์ชันต่ำสุด (MVS) ซึ่งเป็นอัลกอริทึมที่เปิดตัวในระบบโมดูล Go เพื่อแก้ปัญหานี้ MVS สันนิษฐานว่าโมดูลเวอร์ชันใหม่ทั้งหมดเข้ากันได้แบบย้อนหลัง จึงเลือกเวอร์ชันสูงสุดที่ระบุโดยทรัพยากร Dependency (D 1.1 ในตัวอย่างของเรา) โดยเรียกว่า "ต่ำสุด" เนื่องจาก D 1.1 ในที่นี้เป็นเวอร์ชัน ต่ำสุด ที่ตรงตามข้อกำหนดของเรา แม้ว่าจะมี D 1.2 หรือใหม่กว่า แต่เราก็ไม่เลือก ซึ่งมีข้อดีเพิ่มเติมคือการเลือกเวอร์ชันมีความ แม่นยำสูง และ ทำซ้ำได้
ระบบจะทำการแก้ปัญหาเวอร์ชันในเครื่องของคุณ ไม่ใช่รีจิสทรี
ระดับความเข้ากันได้
โปรดทราบว่าข้อสันนิษฐานของ MVS เกี่ยวกับความเข้ากันได้แบบย้อนหลังเป็นไปได้เนื่องจาก MVS จะถือว่าโมดูลเวอร์ชันที่ไม่เข้ากันแบบย้อนหลังเป็นโมดูลแยกต่างหาก ในแง่ของ SemVer หมายความว่า A 1.x และ A 2.x ถือเป็นโมดูลที่แตกต่างกัน และสามารถอยู่ร่วมกันในกราฟทรัพยากร Dependency ที่แก้ปัญหาแล้ว ซึ่งเป็นไปได้เนื่องจากมีการเข้ารหัสเวอร์ชันหลักในเส้นทางแพ็กเกจใน Go จึงไม่มีความขัดแย้งในเวลาคอมไพล์หรือเวลาลิงก์
ใน Bazel เราไม่มีการรับประกันดังกล่าว ดังนั้นเราจึงต้องมีวิธีระบุหมายเลข "เวอร์ชันหลัก" เพื่อตรวจหาเวอร์ชันที่ไม่เข้ากันแบบย้อนหลัง หมายเลขนี้
เรียกว่า ระดับความเข้ากันได้ และโมดูลแต่ละเวอร์ชันจะระบุหมายเลขนี้ใน
คำสั่ง module() เมื่อมีข้อมูลนี้ เราจะแสดงข้อผิดพลาดเมื่อตรวจพบว่ามีโมดูลเวอร์ชันเดียวกันที่มีระดับความเข้ากันได้ต่างกันอยู่ในกราฟทรัพยากร Dependency ที่แก้ปัญหาแล้ว
ชื่อที่เก็บข้อมูล
ใน Bazel ทรัพยากร Dependency ภายนอกทุกรายการจะมีชื่อที่เก็บข้อมูล บางครั้งอาจมีการใช้ทรัพยากร Dependency เดียวกันผ่านชื่อที่เก็บข้อมูลต่างๆ (เช่น ทั้ง
@io_bazel_skylib และ @bazel_skylib หมายถึง
Bazel skylib) หรืออาจมีการใช้ชื่อที่เก็บข้อมูลเดียวกันสำหรับทรัพยากร Dependency ต่างๆ ในโปรเจ็กต์ต่างๆ
ใน Bzlmod โมดูล Bazel และ ส่วนขยายโมดูลสามารถสร้างที่เก็บข้อมูลได้ เราใช้กลไกการแมปที่เก็บข้อมูลในระบบใหม่เพื่อแก้ปัญหาความขัดแย้งของชื่อที่เก็บข้อมูล แนวคิดสำคัญ 2 ประการมีดังนี้
ชื่อที่เก็บข้อมูล Canonical: ชื่อที่เก็บข้อมูลที่ไม่ซ้ำกันทั่วโลกสำหรับ ที่เก็บข้อมูลแต่ละรายการ ชื่อนี้จะเป็นชื่อไดเรกทอรีที่ที่เก็บข้อมูลอยู่
ชื่อนี้สร้างขึ้นดังนี้ (คำเตือน: รูปแบบชื่อ Canonical ไม่ใช่ API ที่คุณควรใช้ เนื่องจากอาจมีการเปลี่ยนแปลงได้ทุกเมื่อ)- สำหรับ repo โมดูล Bazel:
module_name~version
(ตัวอย่าง.@bazel_skylib~1.0.3) - สำหรับ repo ส่วนขยายโมดูล:
module_name~version~extension_name~repo_name
(ตัวอย่าง.@rules_cc~0.0.1~cc_configure~local_config_cc)
- สำหรับ repo โมดูล Bazel:
ชื่อที่เก็บข้อมูลที่ปรากฏ: ชื่อที่เก็บข้อมูลที่จะใช้ในไฟล์
BUILDและ.bzlภายใน repo ทรัพยากร Dependency เดียวกันอาจมีชื่อที่ปรากฏต่างกันใน repo ต่างๆ
ชื่อนี้กำหนดขึ้นดังนี้
ที่เก็บข้อมูลทุกรายการจะมีพจนานุกรมการแมปที่เก็บข้อมูลของทรัพยากร Dependency โดยตรง ซึ่งเป็นการแมปจากชื่อที่เก็บข้อมูลที่ปรากฏไปยังชื่อที่เก็บข้อมูล Canonical
เราใช้การแมปที่เก็บข้อมูลเพื่อแก้ปัญหาชื่อที่เก็บข้อมูลเมื่อสร้างป้ายกำกับ โปรดทราบว่าชื่อที่เก็บข้อมูล Canonical จะไม่ขัดแย้งกัน และคุณสามารถค้นพบการใช้งานชื่อที่เก็บข้อมูลที่ปรากฏได้โดยการแยกวิเคราะห์ไฟล์ MODULE.bazel ดังนั้นจึงตรวจพบและแก้ปัญหาความขัดแย้งได้อย่างง่ายดายโดยไม่ส่งผลกระทบต่อทรัพยากร Dependency อื่นๆ
ทรัพยากร Dependency ที่เข้มงวด
รูปแบบการระบุทรัพยากร Dependency ใหม่ช่วยให้เราทำการตรวจสอบที่เข้มงวดมากขึ้นได้ โดยเฉพาะอย่างยิ่ง ตอนนี้เราบังคับใช้ว่าโมดูลจะใช้ได้เฉพาะ repo ที่สร้างจากทรัพยากร Dependency โดยตรงเท่านั้น ซึ่งจะช่วยป้องกันการหยุดทำงานโดยไม่ตั้งใจและแก้ปัญหาได้ยากเมื่อมีการเปลี่ยนแปลงบางอย่างในกราฟทรัพยากร Dependency แบบทรานซิทีฟ
เราได้ใช้การแมปที่เก็บข้อมูล เพื่อสร้างทรัพยากร Dependency ที่เข้มงวด โดยพื้นฐานแล้ว การแมปที่เก็บข้อมูลสำหรับ repo แต่ละรายการจะมี ทรัพยากร Dependency โดยตรง ทั้งหมด และ repo อื่นๆ จะไม่ปรากฏ ทรัพยากร Dependency ที่ปรากฏสำหรับที่เก็บข้อมูลแต่ละรายการจะกำหนดขึ้นดังนี้
- repo โมดูล Bazel สามารถดู repo ทั้งหมดที่เปิดตัวในไฟล์
MODULE.bazelผ่านbazel_depและuse_repo - repo ส่วนขยายโมดูลสามารถดูทรัพยากร Dependency ที่ปรากฏทั้งหมดของโมดูลที่ให้ส่วนขยาย รวมถึง repo อื่นๆ ทั้งหมดที่สร้างโดยส่วนขยายโมดูลเดียวกัน
รีจิสทรี
Bzlmod ค้นหาทรัพยากร Dependency โดยขอข้อมูลจาก รีจิสทรี Bazel รีจิสทรี Bazel เป็นเพียงฐานข้อมูลของโมดูล Bazel รีจิสทรีที่รองรับมีเพียงรูปแบบ รีจิสทรีดัชนี ซึ่งเป็น ไดเรกทอรีในเครื่องหรือเซิร์ฟเวอร์ HTTP แบบคงที่ที่ใช้รูปแบบเฉพาะ ใน อนาคต เราวางแผนที่จะเพิ่มการรองรับ รีจิสทรีโมดูลเดียว ซึ่งเป็นเพียง repo Git ที่มีซอร์สโค้ดและประวัติของโปรเจ็กต์
รีจิสทรีดัชนี
รีจิสทรีดัชนีเป็นไดเรกทอรีในเครื่องหรือเซิร์ฟเวอร์ HTTP แบบคงที่ที่มีข้อมูลเกี่ยวกับรายการโมดูล ซึ่งรวมถึงหน้าแรก ผู้ดูแล ไฟล์ MODULE.bazel ของแต่ละเวอร์ชัน และวิธีดึงข้อมูลซอร์สโค้ดของแต่ละเวอร์ชัน โดย ไม่ จำเป็นต้องให้บริการไฟล์เก็บถาวรของซอร์สโค้ดเอง
รีจิสทรีดัชนีต้องใช้รูปแบบต่อไปนี้
/bazel_registry.json: ไฟล์ JSON ที่มีข้อมูลเมตาสำหรับรีจิสทรี เช่นmirrorsซึ่งระบุรายการมิเรอร์ที่จะใช้สำหรับไฟล์เก็บถาวรของซอร์สโค้ดmodule_base_pathซึ่งระบุเส้นทางฐานสำหรับโมดูลที่มีประเภทlocal_repositoryในไฟล์source.json
/modules: ไดเรกทอรีที่มีไดเรกทอรีย่อยสำหรับโมดูลแต่ละรายการในรีจิสทรีนี้/modules/$MODULE: ไดเรกทอรีที่มีไดเรกทอรีย่อยสำหรับโมดูลแต่ละเวอร์ชัน รวมถึงไฟล์ต่อไปนี้metadata.json: ไฟล์ JSON ที่มีข้อมูลเกี่ยวกับโมดูล โดยมีช่องต่อไปนี้homepage: URL ของหน้าแรกของโปรเจ็กต์maintainers: รายการออบเจ็กต์ JSON ซึ่งแต่ละรายการสอดคล้องกับข้อมูลของผู้ดูแลโมดูล ในรีจิสทรี โปรดทราบว่าข้อมูลนี้อาจไม่เหมือนกับ ผู้เขียน โปรเจ็กต์versions: รายการโมดูลเวอร์ชันทั้งหมดที่จะพบในรีจิสทรีนี้yanked_versions: รายการโมดูลเวอร์ชันที่ ยกเลิก แล้ว ปัจจุบันการดำเนินการนี้ไม่มีผล แต่ในอนาคตระบบจะข้ามเวอร์ชันที่ยกเลิกแล้วหรือแสดงข้อผิดพลาด
/modules/$MODULE/$VERSION: ไดเรกทอรีที่มีไฟล์ต่อไปนี้MODULE.bazel: ไฟล์MODULE.bazelของโมดูลเวอร์ชันนี้source.json: ไฟล์ JSON ที่มีข้อมูลเกี่ยวกับวิธีดึงข้อมูลซอร์สโค้ดของโมดูลเวอร์ชันนี้- ประเภทเริ่มต้นคือ "archive" ที่มีช่องต่อไปนี้
url: URL ของไฟล์เก็บถาวรของซอร์สโค้ดintegrity: ผลรวมตรวจสอบ Subresource Integrity ของไฟล์เก็บถาวรstrip_prefix: คำนำหน้าไดเรกทอรีที่จะนำออกเมื่อแยกไฟล์เก็บถาวรของซอร์สโค้ดpatches: รายการสตริง ซึ่งแต่ละรายการจะตั้งชื่อไฟล์แพตช์ที่จะใช้กับไฟล์เก็บถาวรที่แยกออกมา ไฟล์แพตช์จะอยู่ในไดเรกทอรี/modules/$MODULE/$VERSION/patchespatch_strip: เหมือนกับอาร์กิวเมนต์--stripของแพตช์ Unix
- คุณสามารถเปลี่ยนประเภทเพื่อใช้เส้นทางในเครื่องที่มีช่องต่อไปนี้ได้
type:local_pathpath: เส้นทางในเครื่องไปยัง repo ซึ่งคำนวณได้ดังนี้- หากเส้นทางเป็นเส้นทางสัมบูรณ์ ระบบจะใช้เส้นทางนั้น
- หากเส้นทางเป็นเส้นทางสัมพัทธ์และ
module_base_pathเป็นเส้นทางสัมบูรณ์ ระบบจะแก้ปัญหาเส้นทางเป็น<module_base_path>/<path> - หากทั้งเส้นทางและ
module_base_pathเป็นเส้นทางสัมพัทธ์ ระบบจะแก้ปัญหาเส้นทางเป็น<registry_path>/<module_base_path>/<path>รีจิสทรีต้องโฮสต์ในเครื่องและใช้โดย--registry=file://<registry_path>มิฉะนั้น Bazel จะแสดงข้อผิดพลาด
- ประเภทเริ่มต้นคือ "archive" ที่มีช่องต่อไปนี้
patches/: ไดเรกทอรีที่ไม่บังคับซึ่งมีไฟล์แพตช์ โดยจะใช้ก็ต่อเมื่อsource.jsonมีประเภท "archive"
รีจิสทรีกลางของ Bazel
รีจิสทรีกลางของ Bazel (BCR) เป็นรีจิสทรีดัชนีที่อยู่ใน
bcr.bazel.build เนื้อหาของรีจิสทรีนี้ได้รับการสนับสนุนโดย repo GitHub
bazelbuild/bazel-central-registry
ชุมชน Bazel เป็นผู้ดูแล BCR และยินดีรับผู้มีส่วนร่วมส่งคำขอ Pull โปรดดู นโยบายและขั้นตอนของรีจิสทรีกลางของ Bazel
นอกเหนือจากการใช้รูปแบบรีจิสทรีดัชนีปกติแล้ว BCR ยังกำหนดให้ต้องมี
ไฟล์ presubmit.yml สำหรับโมดูลแต่ละเวอร์ชัน
(/modules/$MODULE/$VERSION/presubmit.yml) ไฟล์นี้จะระบุเป้าหมายการสร้างและการทดสอบที่สำคัญบางอย่าง
ที่ใช้เพื่อตรวจสอบความถูกต้องของโมดูลเวอร์ชันนี้ และไปป์ไลน์ CI ของ BCR จะใช้ไฟล์นี้เพื่อให้มั่นใจว่าโมดูลใน BCR ทำงานร่วมกันได้
การเลือกรีจิสทรี
คุณสามารถใช้แฟล็ก Bazel --registry ที่ทำซ้ำได้เพื่อระบุรายการรีจิสทรีที่จะขอโมดูลได้ คุณจึงตั้งค่าโปรเจ็กต์ให้ดึงข้อมูลทรัพยากร Dependency จากรีจิสทรีของบุคคลที่สามหรือรีจิสทรีภายในได้ รีจิสทรีที่ระบุไว้ก่อนหน้านี้จะมีลำดับความสำคัญสูงกว่า คุณสามารถใส่รายการแฟล็ก --registry ไว้ในไฟล์ .bazelrc ของโปรเจ็กต์เพื่อความสะดวก
ส่วนขยายโมดูล
ส่วนขยายโมดูลช่วยให้คุณขยายระบบโมดูลได้โดยการอ่านข้อมูลอินพุตจากโมดูลในกราฟทรัพยากร Dependency ทำตรรกะที่จำเป็นเพื่อแก้ปัญหาทรัพยากร Dependency และสร้าง repo โดยการเรียกใช้กฎของ repo ส่วนขยายโมดูลมีฟังก์ชันคล้ายกับมาโคร WORKSPACE ในปัจจุบัน แต่เหมาะกับโลกของโมดูลและทรัพยากร Dependency แบบทรานซิทีฟมากกว่า
ส่วนขยายโมดูลกำหนดไว้ในไฟล์ .bzl เช่นเดียวกับกฎของ repo หรือมาโคร WORKSPACE โดยไม่ได้เรียกใช้โดยตรง แต่โมดูลแต่ละรายการสามารถระบุข้อมูลที่เรียกว่า แท็ก เพื่อให้ส่วนขยายอ่านได้ จากนั้นระบบจะเรียกใช้ส่วนขยายโมดูลหลังจากที่แก้ปัญหาโมดูลเวอร์ชันแล้ว ส่วนขยายแต่ละรายการจะทำงาน 1 ครั้งหลังจากแก้ปัญหาโมดูล (ก่อนที่จะมีการสร้างจริง) และสามารถอ่านแท็กทั้งหมดที่เป็นของส่วนขยายนั้นในกราฟทรัพยากร Dependency ทั้งหมดได้
[ A 1.1 ]
[ * maven.dep(X 2.1) ]
[ * maven.pom(...) ]
/ \
bazel_dep / \ bazel_dep
/ \
[ B 1.2 ] [ C 1.0 ]
[ * maven.dep(X 1.2) ] [ * maven.dep(X 2.1) ]
[ * maven.dep(Y 1.3) ] [ * cargo.dep(P 1.1) ]
\ /
bazel_dep \ / bazel_dep
\ /
[ D 1.4 ]
[ * maven.dep(Z 1.4) ]
[ * cargo.dep(Q 1.1) ]
ในกราฟทรัพยากร Dependency ตัวอย่างด้านบน A 1.1 และ B 1.2 เป็นโมดูล Bazel คุณสามารถคิดว่าแต่ละโมดูลเป็นไฟล์ MODULE.bazel โมดูลแต่ละรายการสามารถระบุแท็กบางรายการสำหรับส่วนขยายโมดูลได้ โดยในที่นี้มีการระบุแท็กบางรายการสำหรับส่วนขยาย "maven" และบางรายการสำหรับ "cargo" เมื่อกราฟทรัพยากร Dependency นี้เสร็จสมบูรณ์ (เช่น B 1.2 อาจมี bazel_dep ใน D 1.3 แต่ได้รับการอัปเกรดเป็น D 1.4 เนื่องจาก C) ระบบจะเรียกใช้ส่วนขยาย "maven" และส่วนขยายนี้จะอ่านแท็ก maven.* ทั้งหมด โดยใช้ข้อมูลในแท็กเพื่อตัดสินใจว่าจะสร้าง repo ใด
เช่นเดียวกับส่วนขยาย "cargo"
การใช้งานส่วนขยาย
ส่วนขยายโฮสต์อยู่ในโมดูล Bazel เอง ดังนั้นหากต้องการใช้ส่วนขยายใน
โมดูล คุณต้องเพิ่ม bazel_dep ในโมดูลนั้นก่อน แล้วจึงเรียกใช้
ฟังก์ชันในตัว use_extension เพื่อนำส่วนขยายนั้นมาใช้ ลองดูตัวอย่างต่อไปนี้ ซึ่งเป็นข้อมูลโค้ดจากไฟล์ MODULE.bazel เพื่อใช้ส่วนขยาย "maven" สมมติที่กำหนดไว้ในโมดูล rules_jvm_external
bazel_dep(name = "rules_jvm_external", version = "1.0")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
หลังจากนำส่วนขยายมาใช้แล้ว คุณสามารถใช้ไวยากรณ์แบบจุดเพื่อระบุแท็กสำหรับส่วนขยายนั้นได้ โปรดทราบว่าแท็กต้องเป็นไปตามสคีมาที่กำหนดโดย
คลาสแท็กที่เกี่ยวข้อง (ดูคำจำกัดความของส่วนขยาย
ด้านล่าง) ตัวอย่างต่อไปนี้แสดงการระบุแท็ก maven.dep และ maven.pom บางรายการ
maven.dep(coord="org.junit:junit:3.0")
maven.dep(coord="com.google.guava:guava:1.2")
maven.pom(pom_xml="//:pom.xml")
หากส่วนขยายสร้าง repo ที่คุณต้องการใช้ในโมดูล ให้ใช้คำสั่ง
use_repo เพื่อประกาศ
repo เหล่านั้น ทั้งนี้เพื่อให้เป็นไปตามเงื่อนไขทรัพยากร Dependency ที่เข้มงวดและหลีกเลี่ยงความขัดแย้งของชื่อ repo ในเครื่อง
use_repo(
maven,
"org_junit_junit",
guava="com_google_guava_guava",
)
repo ที่สร้างโดยส่วนขยายเป็นส่วนหนึ่งของ API ของส่วนขยาย ดังนั้นจากแท็กที่คุณระบุ คุณควรทราบว่าส่วนขยาย "maven" จะสร้าง repo ที่ชื่อ "org_junit_junit" และ repo ที่ชื่อ "com_google_guava_guava" คุณสามารถใช้ use_repo เพื่อเปลี่ยนชื่อ repo เหล่านั้นในขอบเขตของโมดูลได้ เช่น เปลี่ยนเป็น "guava" ในที่นี้
คำจำกัดความของส่วนขยาย
ส่วนขยายโมดูลกำหนดไว้คล้ายกับกฎของ repo โดยใช้ฟังก์ชัน
module_extension
ทั้งสองอย่างมีฟังก์ชันการใช้งาน แต่กฎของ repo มี
แอตทริบิวต์หลายรายการ ส่วนขยายโมดูลมี
tag_classesหลายรายการ ซึ่งแต่ละรายการมี
แอตทริบิวต์หลายรายการ คลาสแท็กกำหนดสคีมาสำหรับแท็กที่ส่วนขยายนี้ใช้ ลองดูตัวอย่างส่วนขยาย "maven" สมมติข้างต้นต่อ
# @rules_jvm_external//:extensions.bzl
maven_dep = tag_class(attrs = {"coord": attr.string()})
maven_pom = tag_class(attrs = {"pom_xml": attr.label()})
maven = module_extension(
implementation=_maven_impl,
tag_classes={"dep": maven_dep, "pom": maven_pom},
)
การประกาศเหล่านี้แสดงให้เห็นอย่างชัดเจนว่าคุณสามารถระบุแท็ก maven.dep และ maven.pom ได้โดยใช้สคีมาแอตทริบิวต์ที่กำหนดไว้ข้างต้น
ฟังก์ชันการใช้งานคล้ายกับมาโคร WORKSPACE ยกเว้นว่าฟังก์ชันนี้จะได้รับออบเจ็กต์ module_ctx ซึ่งให้สิทธิ์เข้าถึงกราฟทรัพยากร Dependency และแท็กที่เกี่ยวข้องทั้งหมด จากนั้นฟังก์ชันการใช้งานควรเรียกใช้กฎของ repo เพื่อสร้าง repo
# @rules_jvm_external//:extensions.bzl
load("//:repo_rules.bzl", "maven_single_jar")
def _maven_impl(ctx):
coords = []
for mod in ctx.modules:
coords += [dep.coord for dep in mod.tags.dep]
output = ctx.execute(["coursier", "resolve", coords]) # hypothetical call
repo_attrs = process_coursier(output)
[maven_single_jar(**attrs) for attrs in repo_attrs]
ในตัวอย่างข้างต้น เราจะดูโมดูลทั้งหมดในกราฟทรัพยากร Dependency
(ctx.modules) ซึ่งแต่ละโมดูลเป็น
bazel_module ออบเจ็กต์ที่ฟิลด์ tags แสดง
แท็ก maven.* ทั้งหมดในโมดูล จากนั้นเราจะเรียกใช้ยูทิลิตี CLI Coursier เพื่อติดต่อ Maven และทำการแก้ปัญหา สุดท้าย เราใช้ผลการแก้ปัญหาเพื่อสร้าง repo หลายรายการโดยใช้กฎของ repo maven_single_jar สมมติ