ส่วนขยายโมดูลช่วยให้ผู้ใช้ขยายระบบโมดูลได้โดยอ่านข้อมูลอินพุตจากโมดูลในกราฟความเกี่ยวข้อง ดำเนินการตามตรรกะที่จำเป็นเพื่อแก้ไขความเกี่ยวข้อง และสุดท้ายสร้างที่เก็บโดยการเรียกใช้กฎของที่เก็บ ส่วนขยายเหล่านี้มีความสามารถคล้ายกับกฎของรีโป ซึ่งช่วยให้สามารถดำเนินการ I/O ของไฟล์ ส่งคำขอเครือข่าย และอื่นๆ ได้ นอกเหนือจากการดำเนินการอื่นๆ แล้ว โมดูลเหล่านี้ยังช่วยให้ Bazel โต้ตอบกับระบบการจัดการแพ็กเกจอื่นๆ ขณะเดียวกันก็เคารพกราฟความเกี่ยวข้องที่สร้างขึ้นจากโมดูล Bazel ด้วย
คุณสามารถกำหนดส่วนขยายของโมดูลในไฟล์ .bzl
ได้เช่นเดียวกับกฎของที่เก็บ โมดูลเหล่านี้ไม่ได้เรียกใช้โดยตรง แต่แต่ละโมดูลจะระบุข้อมูลที่เรียกว่าแท็กเพื่อให้ส่วนขยายอ่าน 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
เพื่อนําที่เก็บข้อมูลที่สร้างขึ้นโดยส่วนขยายมาไว้ในขอบเขตของโมดูลปัจจุบัน
use_repo(maven, "maven")
Repo ที่สร้างขึ้นโดยส่วนขยายเป็นส่วนหนึ่งของ API ของส่วนขยายนั้น ในตัวอย่างนี้ ส่วนขยายโมดูล "maven" จะสร้างที่เก็บข้อมูลชื่อ maven
เมื่อมีการประกาศข้างต้น ส่วนขยายจะแก้ไขป้ายกำกับอย่างถูกต้อง เช่น @maven//:org_junit_junit
เพื่อชี้ไปยังที่เก็บข้อมูลที่สร้างขึ้นโดยส่วนขยาย "maven"
คําจํากัดความของส่วนขยาย
คุณกำหนดส่วนขยายของโมดูลได้เช่นเดียวกับกฎของรีโปโดยใช้ฟังก์ชัน module_extension
อย่างไรก็ตาม แม้ว่ากฎของ repo จะมีแอตทริบิวต์หลายรายการ แต่ส่วนขยายโมดูลจะมีtag_class
ซึ่งแต่ละรายการมีแอตทริบิวต์หลายรายการ คลาสแท็กจะกําหนดสคีมาสําหรับแท็กที่ใช้โดยส่วนขยายนี้ ตัวอย่างเช่น ส่วนขยาย "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
โดยใช้สคีมาแอตทริบิวต์ที่ระบุ
ฟังก์ชันการใช้งานของส่วนขยายโมดูลจะคล้ายกับของกฎในรีพอสิท ยกเว้นว่าจะได้รับออบเจ็กต์ 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
@rules_jvm_external//:extension.bzl
และชื่อ maven
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
การส่งออกส่วนขยายอีกครั้งจากไฟล์ .bzl
อื่นจะทำให้ส่วนขยายมีข้อมูลระบุใหม่ และหากมีการใช้ทั้ง 2 เวอร์ชันของส่วนขยายในกราฟโมดูลแบบทรานซิทีฟ ระบบจะประเมินแยกกันและจะเห็นเฉพาะแท็กที่เชื่อมโยงกับข้อมูลระบุนั้น
ในฐานะผู้เขียนส่วนขยาย คุณควรตรวจสอบว่าผู้ใช้จะใช้ส่วนขยายโมดูลของคุณจากไฟล์ .bzl
ไฟล์เดียวเท่านั้น
ชื่อและระดับการเข้าถึงที่เก็บ
Repo ที่สร้างขึ้นโดยส่วนขยายจะมีชื่อตามแบบฉบับในรูปแบบ module_repo_canonical_name~extension_name~repo_name
สําหรับส่วนขยายที่โฮสต์ในไฟล์ .js ของโมดูลรูท ระบบจะแทนที่ส่วน module_repo_canonical_name
ด้วยสตริง _main
โปรดทราบว่ารูปแบบชื่อที่เป็นทางการไม่ใช่ API ที่คุณควรใช้ เนื่องจากอาจมีการเปลี่ยนแปลงได้ทุกเมื่อ
นโยบายการตั้งชื่อนี้หมายความว่าส่วนขยายแต่ละรายการจะมี "เนมสเปซของที่เก็บข้อมูล" ของตัวเอง ส่วนขยาย 2 รายการที่แตกต่างกันจะกำหนดที่เก็บข้อมูลที่มีชื่อเดียวกันได้โดยไม่เสี่ยงต่อการทับซ้อนกัน และยังหมายความว่า repository_ctx.name
จะรายงานชื่อที่เป็น Canonical ของรีโป ซึ่งไม่เหมือนกับชื่อที่ระบุในการเรียกใช้กฎรีโป
การพิจารณารีโปที่สร้างขึ้นโดยส่วนขยายโมดูลจะมีกฎการแสดงผลของรีโปหลายข้อดังนี้
- Repo โมดูล Bazel จะดู repo ทั้งหมดที่ระบุไว้ในไฟล์
MODULE.bazel
ได้ผ่านbazel_dep
และuse_repo
- รีโปที่สร้างขึ้นโดยส่วนขยายโมดูลจะดูรีโปทั้งหมดที่มองเห็นได้ของโมดูลที่โฮสต์ส่วนขยายนั้น รวมถึงรีโปอื่นๆ ทั้งหมดที่สร้างขึ้นโดยส่วนขยายโมดูลเดียวกัน (โดยใช้ชื่อที่ระบุในการเรียกใช้กฎของรีโปเป็นชื่อที่ปรากฏ)
- ซึ่งอาจทำให้เกิดข้อขัดแย้ง หากที่เก็บโมดูลเห็นที่เก็บที่มีชื่อที่ชัดเจนว่า
foo
และส่วนขยายสร้างที่เก็บที่มีชื่อที่ระบุว่าfoo
foo
จะหมายถึงที่เก็บเดิมสำหรับที่เก็บทั้งหมดที่สร้างโดยส่วนขยายนั้น
- ซึ่งอาจทำให้เกิดข้อขัดแย้ง หากที่เก็บโมดูลเห็นที่เก็บที่มีชื่อที่ชัดเจนว่า
- ในทำนองเดียวกัน ในฟังก์ชันการใช้งานของส่วนขยายโมดูล รีพอสที่สร้างโดยส่วนขยายจะอ้างอิงถึงกันได้โดยใช้ชื่อที่ปรากฏในแอตทริบิวต์ โดยไม่คำนึงถึงลำดับการสร้าง
- ในกรณีที่มีความขัดแย้งกับที่เก็บข้อมูลที่แสดงต่อโมดูล คุณสามารถรวมป้ายกำกับที่ส่งไปยังแอตทริบิวต์กฎของที่เก็บไว้ในการเรียกใช้
Label
เพื่อให้แน่ใจว่าป้ายกำกับดังกล่าวอ้างอิงถึงที่เก็บข้อมูลที่แสดงต่อโมดูลแทนที่เก็บข้อมูลที่สร้างขึ้นจากส่วนขยายซึ่งมีชื่อเดียวกัน
- ในกรณีที่มีความขัดแย้งกับที่เก็บข้อมูลที่แสดงต่อโมดูล คุณสามารถรวมป้ายกำกับที่ส่งไปยังแอตทริบิวต์กฎของที่เก็บไว้ในการเรียกใช้
การลบล้างและการแทรกที่เก็บส่วนขยายโมดูล
โมดูลรูทสามารถใช้ override_repo
และ inject_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")
ตัวอย่าง: แพตช์ Dependency ของ 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
และจะเกิดข้อผิดพลาด
วิธีหลีกเลี่ยงปัญหานี้คือ ให้นำความสามารถในการตั้งชื่อที่เก็บโดยตรงออก หรืออนุญาตให้เฉพาะโมดูลรูทเท่านั้นที่ดำเนินการดังกล่าวได้ การให้สิทธิ์นี้แก่โมดูลรูทนั้นไม่มีปัญหา เนื่องจากไม่มีโมดูลใดต้องอาศัยโมดูลนี้ จึงไม่ต้องกังวลว่าโมดูลอื่นจะสร้างชื่อที่ทับซ้อนกัน