ส่วนขยายของโมดูล

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

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

use_repo(maven, "maven")

Repos ที่ส่วนขยายสร้างขึ้นเป็นส่วนหนึ่งของ API ของส่วนขยายนั้น ในตัวอย่างนี้ ส่วนขยายโมดูล "maven" สัญญาว่าจะสร้างที่เก็บที่เรียกว่า maven ด้วยการประกาศข้างต้น ส่วนขยายจะแก้ไขป้ายกำกับอย่างเหมาะสม เช่น @maven//:org_junit_junit ให้ชี้ไปยังที่เก็บซึ่งส่วนขยาย "maven" สร้างขึ้น

คำจำกัดความของส่วนขยาย

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

# @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 ไฟล์เดียวเท่านั้น

ชื่อที่เก็บและระดับการเข้าถึง

Repos ที่สร้างโดยส่วนขยายจะมีชื่อ Canonical ในรูปแบบ module_repo_canonical_name~extension_name~repo_name สำหรับส่วนขยายที่โฮสต์ในโมดูลรูท ส่วน module_repo_canonical_name จะถูกแทนที่ด้วยสตริง _main โปรดทราบว่ารูปแบบชื่อ Canonical ไม่ใช่ API ที่คุณควรใช้ โดยอาจมีการเปลี่ยนแปลงได้ทุกเมื่อ

นโยบายการตั้งชื่อนี้หมายความว่าส่วนขยายแต่ละรายการมี "เนมสเปซของที่เก็บ" เป็นของตัวเอง ส่วนขยาย 2 ประเภทที่แตกต่างกันสามารถกำหนดที่เก็บที่ใช้ชื่อเดียวกันได้โดยไม่ต้องเสี่ยงในการชนกัน นอกจากนี้ยังหมายความว่า repository_ctx.name จะรายงานชื่อ Canonical ของที่เก็บ ซึ่งไม่เหมือนกับชื่อที่ระบุในการเรียกกฎที่เก็บ

การพิจารณาที่เก็บที่สร้างขึ้นโดยส่วนขยายโมดูลจะมีกฎการเปิดเผยที่เก็บอยู่หลายกฎดังนี้

  • ที่เก็บโมดูล Bazel จะดูที่เก็บทั้งหมดที่แนะนำในไฟล์ MODULE.bazel ได้ ผ่าน bazel_dep และ use_repo
  • ที่เก็บที่สร้างโดยส่วนขยายโมดูลจะเห็นที่เก็บทั้งหมดซึ่งมองเห็นได้ในโมดูลที่โฮสต์ส่วนขยาย บวกที่เก็บอื่นๆ ทั้งหมดที่สร้างขึ้นโดยส่วนขยายโมดูลเดียวกัน (โดยใช้ชื่อที่ระบุในกฎที่เก็บเป็นชื่อที่ปรากฏ)
    • ซึ่งอาจทำให้เกิดการขัดแย้งได้ หากที่เก็บโมดูลเห็นที่เก็บซึ่งมีชื่อชัดเจน foo และส่วนขยายสร้างที่เก็บที่มีชื่อที่ระบุ foo สำหรับที่เก็บทั้งหมดที่สร้างโดยส่วนขยายดังกล่าว foo จะหมายถึงที่เก็บเก่า

แนวทางปฏิบัติแนะนำ

ส่วนนี้จะอธิบายแนวทางปฏิบัติแนะนำเมื่อเขียนส่วนขยายเพื่อให้ใช้งาน บำรุงรักษา และปรับได้อย่างรวดเร็วและเหมาะกับการเปลี่ยนแปลงเมื่อเวลาผ่านไป

ใส่ส่วนขยายแต่ละรายการในไฟล์แยกกัน

เมื่อส่วนขยายอยู่ในไฟล์อื่น ส่วนขยายจะช่วยให้ส่วนขยายหนึ่งสามารถโหลดที่เก็บที่สร้างโดยส่วนขยายอื่นได้ แม้ว่าคุณจะไม่ได้ใช้ฟังก์ชันนี้ วิธีที่ดีที่สุดคือใส่ไว้ในไฟล์แยกต่างหากในกรณีที่ต้องการใช้งานในภายหลัง เนื่องจากการระบุของส่วนขยายจะขึ้นอยู่กับไฟล์ของส่วนขยาย ดังนั้นการย้ายส่วนขยายไปยังไฟล์อื่นในภายหลังจะเปลี่ยนแปลง API สาธารณะของคุณ และจะเป็นการเปลี่ยนแปลงที่เข้ากันไม่ได้แบบย้อนหลังสำหรับผู้ใช้ของคุณ

ระบุระบบปฏิบัติการและสถาปัตยกรรม

หากส่วนขยายของคุณใช้ระบบปฏิบัติการหรือประเภทสถาปัตยกรรมของส่วนขยาย อย่าลืมระบุนโยบายนี้ในคำจำกัดความของส่วนขยายโดยใช้แอตทริบิวต์บูลีน os_dependent และ arch_dependent ซึ่งช่วยให้ Bazel ตระหนักได้ว่า จำเป็นต้องมีการประเมินซ้ำหากมีการเปลี่ยนแปลงอย่างใดอย่างหนึ่ง

เฉพาะโมดูลรูทเท่านั้นที่ควรมีผลต่อชื่อที่เก็บโดยตรง

โปรดทราบว่าเมื่อส่วนขยายสร้างที่เก็บ ระบบจะสร้างที่เก็บภายในเนมสเปซของส่วนขยาย ซึ่งหมายความว่าการขัดแย้งอาจเกิดขึ้นหากโมดูลต่างๆ ใช้ส่วนขยายเดียวกันและจบลงด้วยการสร้างที่เก็บที่มีชื่อเดียวกัน ซึ่งมักจะแสดงเป็น tag_class ของส่วนขยายโมดูลที่มีอาร์กิวเมนต์ name ที่ส่งเป็นค่า name ของกฎที่เก็บ

ตัวอย่างเช่น สมมติว่าโมดูลราก A ขึ้นอยู่กับโมดูล B ทั้ง 2 โมดูล ขึ้นอยู่กับโมดูล mylang หากทั้ง A และ B เรียกใช้ mylang.toolchain(name="foo") ทั้งคู่จะพยายามสร้างที่เก็บชื่อ foo ภายในโมดูล mylang และเกิดข้อผิดพลาดขึ้น

เพื่อหลีกเลี่ยงปัญหานี้ ให้นำความสามารถในการตั้งชื่อที่เก็บโดยตรงออก หรืออนุญาตให้ใช้เฉพาะโมดูลรูทเท่านั้น การอนุญาตให้โมดูลรูทมีความสามารถในการดำเนินการนี้ได้ เพราะจะไม่มีการพึ่งพาโมดูล ดังนั้น จึงไม่ต้องกังวลว่าโมดูลอื่นจะสร้างชื่อที่ขัดแย้งกัน