การโต้ตอบกับ Bazel ในแต่ละวันส่วนใหญ่จะเกิดขึ้นผ่านคำสั่ง 2-3 คำสั่ง ได้แก่
build, test และ run แต่ในบางครั้ง คุณอาจรู้สึกว่าฟีเจอร์เหล่านี้มีข้อจำกัด เช่น คุณอาจต้องการ
พุชแพ็กเกจไปยังที่เก็บ เผยแพร่เอกสารสำหรับผู้ใช้ปลายทาง หรือ
ติดตั้งใช้งานแอปพลิเคชันด้วย Kubernetes แต่ Bazel ไม่มีคำสั่ง publish หรือ
deploy แล้วการดำเนินการเหล่านี้จะอยู่ที่ไหน
คำสั่ง bazel run
การที่ Bazel มุ่งเน้นความสมบูรณ์ ความสามารถในการทำซ้ำ และการเพิ่มขึ้นหมายความว่าคำสั่ง build และ test ไม่เป็นประโยชน์สำหรับงานข้างต้น การดำเนินการเหล่านี้
อาจทำงานในแซนด์บ็อกซ์ โดยมีการเข้าถึงเครือข่ายแบบจำกัด และไม่รับประกันว่าจะ
เรียกใช้ซ้ำทุกครั้งที่มี bazel build
แต่ให้ใช้ bazel run แทน ซึ่งเป็นฟังก์ชันที่ใช้สำหรับงานที่คุณต้องการให้มีผลข้างเคียง ผู้ใช้ Bazel คุ้นเคยกับกฎที่สร้างไฟล์ที่เรียกใช้งานได้ และ
ผู้เขียนกฎสามารถทำตามชุดรูปแบบทั่วไปเพื่อขยายกฎนี้ไปยัง
"คำกริยาที่กำหนดเอง" ได้
ในสภาพแวดล้อมจริง: rules_k8s
ตัวอย่างเช่น ลองพิจารณา rules_k8s
ซึ่งเป็นกฎ Kubernetes สำหรับ Bazel สมมติว่าคุณมีเป้าหมายต่อไปนี้
# BUILD file in //application/k8s
k8s_object(
name = "staging",
kind = "deployment",
cluster = "testing",
template = "deployment.yaml",
)
k8s_object กฎจะสร้างไฟล์ YAML ของ Kubernetes มาตรฐานเมื่อใช้ bazel build ในเป้าหมาย staging
อย่างไรก็ตาม k8s_object
มาโครจะสร้างเป้าหมายเพิ่มเติมด้วย โดยมีชื่อ เช่น staging.apply และ :staging.delete สคริปต์เหล่านี้จะสร้าง
เพื่อดำเนินการดังกล่าว และเมื่อเรียกใช้ด้วย bazel run
staging.apply สคริปต์เหล่านี้จะทำงานเหมือนคำสั่ง bazel k8s-apply หรือ bazel
k8s-delete ของเราเอง
อีกตัวอย่าง: ts_api_guardian_test
รูปแบบนี้ยังพบได้ในโปรเจ็กต์ Angular ด้วย มาโคร
ts_api_guardian_test
จะสร้างเป้าหมาย 2 รายการ อย่างแรกคือnodejs_testเป้าหมายมาตรฐานซึ่งเปรียบเทียบ
เอาต์พุตที่สร้างขึ้นบางส่วนกับไฟล์ "golden" (นั่นคือไฟล์ที่มี
เอาต์พุตที่คาดไว้) ซึ่งสร้างและเรียกใช้ได้ด้วยการเรียกใช้ bazel
test ปกติ ใน angular-cli คุณสามารถเรียกใช้เป้าหมายดังกล่าว
ด้วย bazel test //etc/api:angular_devkit_core_api
เมื่อเวลาผ่านไป คุณอาจต้องอัปเดตไฟล์ทองคำนี้ด้วยเหตุผลที่ถูกต้อง
การอัปเดตด้วยตนเองนั้นน่าเบื่อและมีโอกาสเกิดข้อผิดพลาดสูง ดังนั้นมาโครนี้จึงมีnodejs_binaryเป้าหมายที่อัปเดตไฟล์อ้างอิงแทนที่จะเปรียบเทียบกับไฟล์อ้างอิงด้วย กล่าวคือ คุณสามารถเขียนสคริปต์ทดสอบเดียวกันให้ทำงานในโหมด "ยืนยัน"
หรือ "ยอมรับ" ได้โดยขึ้นอยู่กับวิธีเรียกใช้ ซึ่งเป็นไปตามรูปแบบเดียวกันกับที่คุณได้เรียนรู้แล้ว นั่นคือไม่มีคำสั่ง bazel test-accept ในตัว แต่คุณสามารถสร้างเอฟเฟกต์เดียวกันได้ด้วย bazel run //etc/api:angular_devkit_core_api.accept
รูปแบบนี้มีประสิทธิภาพมาก และพบได้บ่อยเมื่อคุณ เรียนรู้ที่จะสังเกต
การปรับกฎของคุณเอง
มาโครคือหัวใจสำคัญของรูปแบบนี้ มาโครจะใช้เหมือน กฎ แต่สร้างเป้าหมายได้หลายรายการ โดยปกติแล้วจะสร้าง เป้าหมายที่มีชื่อที่ระบุซึ่งดำเนินการสร้างหลัก เช่น สร้างไบนารีปกติ อิมเมจ Docker หรือที่เก็บถาวรของซอร์สโค้ด ในรูปแบบนี้ ระบบจะสร้างเป้าหมายเพิ่มเติมเพื่อสร้างสคริปต์ที่ทำงานแบบผลข้างเคียงตามเอาต์พุตของเป้าหมายหลัก เช่น การเผยแพร่ไบนารีที่ได้หรือการอัปเดตเอาต์พุตการทดสอบที่คาดไว้
เพื่อแสดงให้เห็นภาพ ให้สร้างกฎสมมติที่สร้างเว็บไซต์ด้วย Sphinx โดยใช้มาโครเพื่อสร้างเป้าหมายเพิ่มเติม ที่อนุญาตให้ผู้ใช้เผยแพร่เมื่อพร้อม พิจารณากฎที่มีอยู่ต่อไปนี้ สำหรับการสร้างเว็บไซต์ด้วย Sphinx
_sphinx_site = rule(
implementation = _sphinx_impl,
attrs = {"srcs": attr.label_list(allow_files = [".rst"])},
)
จากนั้นพิจารณากฎต่อไปนี้ ซึ่งจะสร้างสคริปต์ที่เมื่อเรียกใช้แล้ว จะเผยแพร่หน้าที่สร้างขึ้น
_sphinx_publisher = rule(
implementation = _publish_impl,
attrs = {
"site": attr.label(),
"_publisher": attr.label(
default = "//internal/sphinx:publisher",
executable = True,
),
},
executable = True,
)
สุดท้าย ให้กำหนดมาโครสัญลักษณ์ต่อไปนี้ (พร้อมใช้งานใน Bazel 8 ขึ้นไป) เพื่อ สร้างเป้าหมายสำหรับกฎทั้ง 2 ข้อข้างต้นพร้อมกัน
def _sphinx_site_impl(name, visibility, srcs, **kwargs):
# This creates the primary target, producing the Sphinx-generated HTML. We
# set `visibility = visibility` to make it visible to callers of the
# macro.
_sphinx_site(name = name, visibility = visibility, srcs = srcs, **kwargs)
# This creates the secondary target, which produces a script for publishing
# the site generated above. We don't want it to be visible to callers of
# our macro, so we omit visibility for it.
_sphinx_publisher(name = "%s.publish" % name, site = name, **kwargs)
sphinx_site = macro(
implementation = _sphinx_site_impl,
attrs = {"srcs": attr.label_list(allow_files = [".rst"])},
# Inherit common attributes like tags and testonly
inherit_attrs = "common",
)
หรือหากต้องการรองรับ Bazel เวอร์ชันที่เก่ากว่า Bazel 8 คุณจะต้อง กำหนดมาโครเดิมแทน
def sphinx_site(name, srcs = [], **kwargs):
# This creates the primary target, producing the Sphinx-generated HTML.
_sphinx_site(name = name, srcs = srcs, **kwargs)
# This creates the secondary target, which produces a script for publishing
# the site generated above.
_sphinx_publisher(name = "%s.publish" % name, site = name, **kwargs)
ในไฟล์ BUILD ให้ใช้มาโครราวกับว่ามาโครสร้างเป้าหมายหลักเท่านั้น
sphinx_site(
name = "docs",
srcs = ["index.md", "providers.md"],
)
ในตัวอย่างนี้ ระบบจะสร้างเป้าหมาย "docs" เหมือนกับว่ามาโครเป็นกฎ Bazel มาตรฐานแบบเดี่ยว
เมื่อสร้างแล้ว กฎจะสร้างการกำหนดค่าบางอย่าง
และเรียกใช้ Sphinx เพื่อสร้างเว็บไซต์ HTML ซึ่งพร้อมสำหรับการตรวจสอบด้วยตนเอง อย่างไรก็ตาม
ระบบจะสร้างเป้าหมาย "docs.publish" เพิ่มเติมด้วย ซึ่งจะสร้างสคริปต์สำหรับ
การเผยแพร่เว็บไซต์ เมื่อตรวจสอบเอาต์พุตของเป้าหมายหลักแล้ว คุณจะ
ใช้ bazel run :docs.publish เพื่อเผยแพร่ต่อสาธารณะได้ เช่นเดียวกับ
คำสั่ง bazel publish ที่สมมติขึ้น
แต่ยังไม่ทราบชัดเจนทันทีว่าการใช้งาน_sphinx_publisher
กฎอาจมีลักษณะอย่างไร โดยส่วนใหญ่แล้ว การดำเนินการเช่นนี้จะเขียนสคริปต์เชลล์ของตัวเรียกใช้
โดยปกติแล้ววิธีนี้จะเกี่ยวข้องกับการใช้
ctx.actions.expand_template
เพื่อเขียนสคริปต์เชลล์ที่เรียบง่ายมาก ในกรณีนี้คือการเรียกใช้ไบนารีของผู้เผยแพร่
พร้อมเส้นทางไปยังเอาต์พุตของเป้าหมายหลัก วิธีนี้จะช่วยให้การติดตั้งใช้งานของผู้เผยแพร่โฆษณายังคงเป็นแบบทั่วไปได้ _sphinx_site กฎจะสร้าง HTML ได้เท่านั้น และสคริปต์ขนาดเล็กนี้เป็นสิ่งเดียวที่จำเป็นในการรวมทั้ง 2 อย่างเข้าด้วยกัน
ใน rules_k8s .apply จะทำดังนี้
expand_template
เขียนสคริปต์ Bash ที่เรียบง่ายมากโดยอิงตาม
apply.sh.tpl
ซึ่งเรียกใช้ kubectl พร้อมเอาต์พุตของเป้าหมายหลัก จากนั้นจึงสร้างและเรียกใช้สคริปต์นี้ด้วย bazel run :staging.apply ซึ่งจะให้คำสั่ง k8s-apply สำหรับเป้าหมาย k8s_object ได้อย่างมีประสิทธิภาพ