การใช้มาโครเพื่อสร้างคำกริยาที่กำหนดเอง

การโต้ตอบกับ 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

เมื่อเวลาผ่านไป คุณอาจต้องอัปเดตไฟล์ golden ด้วยเหตุผลที่สมควร การอัปเดตไฟล์นี้ด้วยตนเองเป็นเรื่องน่าเบื่อและอาจเกิดข้อผิดพลาดได้ ดังนั้นมาโครนี้จึงมีเป้าหมาย nodejs_binary ที่อัปเดตไฟล์ golden แทนที่จะเปรียบเทียบกับไฟล์ดังกล่าว กล่าวคือ คุณสามารถเขียนสคริปต์การทดสอบเดียวกันให้ทํางานในโหมด "verify" หรือ "accept" ได้โดยขึ้นอยู่กับวิธีเรียกใช้ ซึ่งเป็นไปตามรูปแบบเดียวกับที่คุณได้เรียนรู้ไปแล้ว นั่นคือไม่มีคําสั่ง 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,
)

สุดท้าย ให้กําหนดมาโครต่อไปนี้เพื่อสร้างเป้าหมายสําหรับกฎทั้ง 2 ข้อข้างต้นด้วยกัน

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 อาจดูไม่ชัดเจนในทันที โดยปกติแล้ว การดําเนินการเช่นนี้จะเขียนสคริปต์ของ Shell launcher วิธีนี้มักเกี่ยวข้องกับการใช้ ctx.actions.expand_template เพื่อเขียนสคริปต์ของ Shell ที่เรียบง่ายมาก ในกรณีนี้คือการเรียกใช้ไบนารีของผู้เผยแพร่โฆษณา ด้วยเส้นทางไปยังเอาต์พุตของเป้าหมายหลัก วิธีนี้จะช่วยให้การใช้งานของผู้เผยแพร่โฆษณายังคงเป็นแบบทั่วไป กฎ _sphinx_site สามารถสร้าง HTML ได้ และสคริปต์ขนาดเล็กนี้เป็นสิ่งเดียวที่จําเป็นในการรวมทั้ง 2 อย่างเข้าด้วยกัน

ใน rules_k8s สิ่งที่ .apply ทําคือ expand_template จะเขียนสคริปต์ Bash ที่เรียบง่ายมากโดยอิงตาม apply.sh.tpl, ซึ่งเรียกใช้ kubectl ด้วยเอาต์พุตของเป้าหมายหลัก จากนั้นคุณสามารถสร้างและเรียกใช้สคริปต์นี้ด้วย bazel run :staging.apply ซึ่งจะให้คําสั่ง k8s-apply สําหรับเป้าหมาย k8s_object