ส่วนขยายโมดูลช่วยให้ผู้ใช้ขยายระบบโมดูลได้โดยการอ่านข้อมูลอินพุตจากโมดูลต่างๆ ในกราฟการขึ้นต่อกัน ดำเนินการตรรกะที่จำเป็นเพื่อแก้ปัญหาการขึ้นต่อกัน และสุดท้ายสร้าง repo โดยการเรียกใช้กฎของ repo ส่วนขยายเหล่านี้มีความสามารถคล้ายกับกฎของ repo ซึ่งช่วยให้ดำเนินการ I/O ของไฟล์ ส่งคำขอเครือข่าย และอื่นๆ ได้ นอกจากนี้ ยังช่วยให้ Bazel โต้ตอบกับระบบการจัดการแพ็กเกจอื่นๆ ได้ในขณะที่ยังคงปฏิบัติตามกราฟการขึ้นต่อกันที่สร้างจากโมดูล Bazel
คุณสามารถกำหนดส่วนขยายโมดูลในไฟล์ .bzl ได้เช่นเดียวกับกฎของ repo โดยจะไม่มีการเรียกใช้โดยตรง แต่โมดูลแต่ละโมดูลจะระบุข้อมูลบางส่วนที่เรียกว่า แท็กเพื่อให้ส่วนขยายอ่าน Bazel จะเรียกใช้การแก้ปัญหาโมดูลก่อนที่จะประเมินส่วนขยาย ส่วนขยายจะอ่านแท็กทั้งหมดที่เป็นของส่วนขยายนั้นในกราฟการขึ้นต่อกันทั้งหมด
การใช้งานส่วนขยาย
ส่วนขยายจะโฮสต์อยู่ในโมดูล Bazel เอง หากต้องการใช้ส่วนขยายในโมดูล ให้เพิ่ม bazel_dep ในโมดูลที่โฮสต์ส่วนขยายก่อน แล้วเรียกใช้ use_extension ฟังก์ชันในตัว เพื่อนำส่วนขยายนั้นมาไว้ในขอบเขต ดูตัวอย่างต่อไปนี้ ซึ่งเป็นข้อมูลโค้ดจากไฟล์
MODULE.bazel เพื่อใช้ส่วนขยาย "maven" ที่กำหนดไว้ใน
rules_jvm_external
โมดูล:
bazel_dep(name = "rules_jvm_external", version = "4.5")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
ซึ่งจะผูกค่าที่แสดงผลของ use_extension กับตัวแปร ซึ่งช่วยให้ผู้ใช้ระบุแท็กสำหรับส่วนขยายได้โดยใช้ไวยากรณ์แบบจุด แท็กต้องเป็นไปตาม
สคีมาที่กำหนดโดย คลาสแท็กที่เกี่ยวข้องซึ่งระบุไว้ใน
คำจำกัดความของส่วนขยาย ตัวอย่างการระบุแท็ก maven.install และ maven.artifact บางรายการ
maven.install(artifacts = ["org.junit:junit:4.13.2"])
maven.artifact(group = "com.google.guava",
artifact = "guava",
version = "27.0-jre",
exclusions = ["com.google.j2objc:j2objc-annotations"])
ใช้คำสั่ง use_repo เพื่อนำ repo ที่ส่วนขยายสร้างขึ้นมาไว้ในขอบเขตของโมดูลปัจจุบัน
use_repo(maven, "maven")
repo ที่ส่วนขยายสร้างขึ้นเป็นส่วนหนึ่งของ API ของส่วนขยาย ในตัวอย่างนี้ ส่วนขยายโมดูล "maven" สัญญาว่าจะสร้าง repo ที่ชื่อว่า maven เมื่อมีการประกาศข้างต้น ส่วนขยายจะแก้ปัญหากำหนดป้ายกำกับอย่างถูกต้อง เช่น @maven//:org_junit_junit เพื่อชี้ไปยัง repo ที่ส่วนขยาย "maven" สร้างขึ้น
คำจำกัดความของส่วนขยาย
คุณสามารถกำหนดส่วนขยายโมดูลได้ในลักษณะเดียวกับกฎของ repo โดยใช้ฟังก์ชัน
module_extension อย่างไรก็ตาม
ในขณะที่กฎของ repo มีแอตทริบิวต์หลายรายการ ส่วนขยายโมดูลมี
tag_classes ซึ่งแต่ละรายการมีแอตทริบิวต์หลายรายการ
คลาสแท็กกำหนดสคีมาสำหรับแท็กที่ส่วนขยายนี้ใช้ ตัวอย่างเช่น ส่วนขยาย "maven" ด้านบนอาจกำหนดไว้ดังนี้
# @rules_jvm_external//:extensions.bzl
_install = tag_class(attrs = {"artifacts": attr.string_list(), ...})
_artifact = tag_class(attrs = {"group": attr.string(), "artifact": attr.string(), ...})
maven = module_extension(
implementation = _maven_impl,
tag_classes = {"install": _install, "artifact": _artifact},
)
การประกาศเหล่านี้แสดงว่าสามารถระบุแท็ก maven.install และ maven.artifact ได้โดยใช้สคีมาแอตทริบิวต์ที่ระบุ
ฟังก์ชันการใช้งานของส่วนขยายโมดูลจะคล้ายกับฟังก์ชันการใช้งานของกฎของ repo
ยกเว้นว่าฟังก์ชันการใช้งานของส่วนขยายโมดูลจะได้รับออบเจ็กต์ module_ctx ซึ่งให้สิทธิ์เข้าถึงโมดูลทั้งหมดที่ใช้ส่วนขยายและแท็กที่เกี่ยวข้องทั้งหมด
จากนั้นฟังก์ชันการใช้งานจะเรียกใช้กฎของ repo เพื่อสร้าง repo
# @rules_jvm_external//:extensions.bzl
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") # a repo rule
def _maven_impl(ctx):
# This is a fake implementation for demonstration purposes only
# collect artifacts from across the dependency graph
artifacts = []
for mod in ctx.modules:
for install in mod.tags.install:
artifacts += install.artifacts
artifacts += [_to_artifact(artifact) for artifact in mod.tags.artifact]
# call out to the coursier CLI tool to resolve dependencies
output = ctx.execute(["coursier", "resolve", artifacts])
repo_attrs = _process_coursier_output(output)
# call repo rules to generate repos
for attrs in repo_attrs:
http_file(**attrs)
_generate_hub_repo(name = "maven", repo_attrs)
ข้อมูลระบุตัวตนของส่วนขยาย
ระบบจะระบุส่วนขยายโมดูลตามชื่อและไฟล์ .bzl ที่ปรากฏในการเรียกใช้ use_extension ในตัวอย่างต่อไปนี้ ระบบจะระบุส่วนขยาย maven
ตามไฟล์ .bzl และ
ชื่อ maven@rules_jvm_external//:extension.bzl
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
การส่งออกส่วนขยายจากไฟล์ .bzl อื่นอีกครั้งจะทำให้ส่วนขยายมีข้อมูลระบุตัวตนใหม่ และหากมีการใช้ส่วนขยายทั้ง 2 เวอร์ชันในกราฟโมดูลแบบทรานซิทีฟ ระบบจะประเมินส่วนขยายทั้ง 2 เวอร์ชันแยกกันและจะเห็นเฉพาะแท็กที่เชื่อมโยงกับข้อมูลระบุตัวตนนั้นๆ
ในฐานะผู้เขียนส่วนขยาย คุณควรตรวจสอบว่าผู้ใช้จะใช้ส่วนขยายโมดูลของคุณจากไฟล์ .bzl ไฟล์เดียวเท่านั้น
ชื่อที่เก็บข้อมูลและการเข้าถึง
repo ที่ส่วนขยายสร้างขึ้นจะมีชื่อมาตรฐานในรูปแบบ module_repo_canonical_name+extension_name+repo_name โปรดทราบว่ารูปแบบชื่อ Canonical ไม่ใช่ API ที่คุณควรใช้ เนื่องจากอาจมีการเปลี่ยนแปลงได้ทุกเมื่อ
นโยบายการตั้งชื่อนี้หมายความว่าส่วนขยายแต่ละรายการจะมี "เนมสเปซ repo" เป็นของตัวเอง ส่วนขยาย 2 รายการที่แตกต่างกันสามารถกำหนด repo ที่มีชื่อเดียวกันได้โดยไม่ต้องเสี่ยงต่อการเกิดข้อขัดแย้ง นอกจากนี้ยังหมายความว่า repository_ctx.name จะรายงานชื่อมาตรฐานของ repo ซึ่ง ไม่ เหมือนกับชื่อที่ระบุในการเรียกใช้กฎของ repo
เมื่อพิจารณา repo ที่ส่วนขยายโมดูลสร้างขึ้นแล้ว จะมีกฎการเข้าถึง repo หลายข้อดังนี้
- repo โมดูล Bazel สามารถดู repo ทั้งหมดที่ระบุไว้ในไฟล์
MODULE.bazelผ่านbazel_depและuse_repo - repo ที่ส่วนขยายโมดูลสร้างขึ้นสามารถดู repo ทั้งหมดที่โมดูลที่โฮสต์ส่วนขยายมองเห็น รวมถึง repo อื่นๆ ทั้งหมดที่ส่วนขยายโมดูลเดียวกันสร้างขึ้น (โดยใช้ชื่อที่ระบุในการเรียกใช้กฎของ repo เป็นชื่อที่ปรากฏ)
- ซึ่งอาจทำให้เกิดข้อขัดแย้ง หาก repo โมดูลสามารถดู repo ที่มีชื่อที่ปรากฏ
fooและส่วนขยายสร้าง repo ที่มีชื่อที่ระบุfooแสดงว่า repo ทั้งหมดที่ส่วนขยายนั้นสร้างขึ้นfooจะอ้างอิงถึง repo แรก
- ซึ่งอาจทำให้เกิดข้อขัดแย้ง หาก repo โมดูลสามารถดู repo ที่มีชื่อที่ปรากฏ
- ในทำนองเดียวกัน ในฟังก์ชันการใช้งานของส่วนขยายโมดูล repo ที่ส่วนขยายสร้างขึ้นสามารถอ้างอิงถึงกันได้ด้วยชื่อที่ปรากฏในแอตทริบิวต์ โดยไม่คำนึงถึงลำดับที่สร้าง
- ในกรณีที่เกิดข้อขัดแย้งกับที่เก็บข้อมูลที่โมดูลมองเห็นได้ คุณสามารถใส่ป้ายกำกับ
ที่ส่งไปยังแอตทริบิวต์กฎของที่เก็บข้อมูลไว้ในการเรียกใช้
Labelเพื่อให้แน่ใจว่าป้ายกำกับเหล่านั้นอ้างอิงถึง repo ที่โมดูลมองเห็นได้แทนที่จะเป็น repo ที่ส่วนขยายสร้างขึ้น ซึ่งมีชื่อเดียวกัน
- ในกรณีที่เกิดข้อขัดแย้งกับที่เก็บข้อมูลที่โมดูลมองเห็นได้ คุณสามารถใส่ป้ายกำกับ
ที่ส่งไปยังแอตทริบิวต์กฎของที่เก็บข้อมูลไว้ในการเรียกใช้
การลบล้างและการแทรก repo ส่วนขยายโมดูล
โมดูลรูทสามารถใช้
override_repo และ
inject_repo เพื่อลบล้างหรือแทรก
repo ส่วนขยายโมดูล
ตัวอย่าง: การแทนที่ java_tools ของ rules_java ด้วยสำเนาที่จัดจำหน่าย
# MODULE.bazel
local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository")
local_repository(
name = "my_java_tools",
path = "vendor/java_tools",
)
bazel_dep(name = "rules_java", version = "7.11.1")
java_toolchains = use_extension("@rules_java//java:extension.bzl", "toolchains")
override_repo(java_toolchains, remote_java_tools = "my_java_tools")
ตัวอย่าง: การแพตช์การขึ้นต่อกันของ Go เพื่อให้ขึ้นต่อกันกับ @zlib แทนที่จะเป็น zlib ของระบบ
# MODULE.bazel
bazel_dep(name = "gazelle", version = "0.38.0")
bazel_dep(name = "zlib", version = "1.3.1.bcr.3")
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
go_deps.module_override(
patches = [
"//patches:my_module_zlib.patch",
],
path = "example.com/my_module",
)
use_repo(go_deps, ...)
inject_repo(go_deps, "zlib")
# patches/my_module_zlib.patch
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,6 +1,6 @@
go_binary(
name = "my_module",
importpath = "example.com/my_module",
srcs = ["my_module.go"],
- copts = ["-lz"],
+ cdeps = ["@zlib"],
)
แนวทางปฏิบัติแนะนำ
ส่วนนี้อธิบายแนวทางปฏิบัติแนะนำเมื่อเขียนส่วนขยายเพื่อให้ใช้งานง่าย บำรุงรักษาได้ และปรับตัวเข้ากับการเปลี่ยนแปลงได้ดีเมื่อเวลาผ่านไป
ใส่ส่วนขยายแต่ละรายการในไฟล์แยกกัน
เมื่อส่วนขยายอยู่ในไฟล์ที่ต่างกัน ส่วนขยายหนึ่งจะโหลดที่เก็บข้อมูลที่ส่วนขยายอื่นสร้างขึ้นได้ แม้ว่าคุณจะไม่ได้ใช้ฟังก์ชันนี้ แต่ควรใส่ส่วนขยายไว้ในไฟล์แยกกันในกรณีที่จำเป็นต้องใช้ในภายหลัง เนื่องจากข้อมูลระบุตัวตนของส่วนขยายอิงตามไฟล์ของส่วนขยาย ดังนั้นการย้ายส่วนขยายไปยังไฟล์อื่นในภายหลังจะเปลี่ยน API สาธารณะและเป็นการเปลี่ยนแปลงที่เข้ากันไม่ได้กับเวอร์ชันก่อนหน้าสำหรับผู้ใช้
ระบุความสามารถในการทำซ้ำ
หากส่วนขยายของคุณกำหนดที่เก็บข้อมูลเดียวกันเสมอเมื่อได้รับอินพุตเดียวกัน
(แท็กส่วนขยาย ไฟล์ที่อ่าน ฯลฯ) และโดยเฉพาะอย่างยิ่งไม่ได้ขึ้นอยู่กับ
การดาวน์โหลดใดๆ ที่ไม่มีการป้องกันด้วย
เช็คซัม ให้พิจารณาแสดงผล
extension_metadata ที่มี
reproducible = True. ซึ่งจะช่วยให้ Bazel ข้ามส่วนขยายนี้เมื่อเขียนไปยังไฟล์ล็อก
ระบุระบบปฏิบัติการและสถาปัตยกรรม
หากส่วนขยายของคุณขึ้นอยู่กับระบบปฏิบัติการหรือประเภทสถาปัตยกรรมของระบบปฏิบัติการ ให้ระบุไว้ในคำจำกัดความของส่วนขยายโดยใช้แอตทริบิวต์บูลีน os_dependent และ arch_dependent ซึ่งจะช่วยให้ Bazel รับรู้ถึงความจำเป็นในการประเมินซ้ำหากมีการเปลี่ยนแปลงระบบปฏิบัติการหรือสถาปัตยกรรม
เนื่องจากการขึ้นต่อกันกับโฮสต์ในลักษณะนี้ทำให้การรักษา ข้อมูลไฟล์ล็อกสำหรับส่วนขยายนี้ยากขึ้น ให้พิจารณา ทำเครื่องหมายส่วนขยายว่าทำซ้ำได้หากเป็นไปได้
เฉพาะโมดูลรูทเท่านั้นที่ควรส่งผลต่อชื่อที่เก็บข้อมูลโดยตรง
โปรดทราบว่าเมื่อส่วนขยายสร้างที่เก็บข้อมูล ระบบจะสร้างที่เก็บข้อมูลเหล่านั้นภายในเนมสเปซของส่วนขยาย ซึ่งหมายความว่าอาจเกิดข้อขัดแย้งได้หากโมดูลต่างๆ ใช้ส่วนขยายเดียวกันและสร้างที่เก็บข้อมูลที่มีชื่อเดียวกัน ซึ่งมักจะแสดงให้เห็นว่า tag_class ของส่วนขยายโมดูลมีอาร์กิวเมนต์ name ที่ส่งเป็นค่า name ของกฎที่เก็บข้อมูล
ตัวอย่างเช่น สมมติว่าโมดูลรูท A ขึ้นอยู่กับโมดูล B ทั้ง 2 โมดูลขึ้นอยู่กับโมดูล mylang หากทั้ง A และ B เรียกใช้
mylang.toolchain(name="foo") ทั้ง 2 โมดูลจะพยายามสร้างที่เก็บข้อมูลที่ชื่อว่า
foo ภายในโมดูล mylang และจะเกิดข้อผิดพลาด
หากต้องการหลีกเลี่ยงปัญหานี้ ให้นำความสามารถในการตั้งชื่อที่เก็บข้อมูลโดยตรงออก หรืออนุญาตให้เฉพาะโมดูลรูทเท่านั้นที่ทำได้ การอนุญาตให้โมดูลรูทมีความสามารถนี้ถือเป็นเรื่องปกติเนื่องจากจะไม่มีสิ่งใดขึ้นอยู่กับโมดูลรูท ดังนั้นโมดูลรูทจึงไม่ต้องกังวลว่าโมดูลอื่นจะสร้างชื่อที่ขัดแย้ง