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

7.3 · 7.2 · 7.1 · 7.0 · 6.5

การโต้ตอบแบบวันต่อวันกับ 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 มาตรฐาน ซึ่งจะเปรียบเทียบเอาต์พุตที่สร้างขึ้นบางรายการกับไฟล์ "สีทอง" (ซึ่งก็คือไฟล์ที่มีเอาต์พุตที่คาดไว้) ซึ่งสร้างและเรียกใช้ได้ด้วยการเรียกใช้ bazel test ปกติ ใน angular-cli คุณสามารถเรียกใช้เป้าหมายดังกล่าว 1 รายการด้วย 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,
)

สุดท้าย ให้กำหนดมาโครต่อไปนี้เพื่อสร้างเป้าหมายสำหรับกฎทั้ง 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"],
)

ในตัวอย่างนี้ ระบบจะสร้างเป้าหมาย "เอกสาร" เช่นเดียวกับที่มาโครเป็นกฎ Bazel มาตรฐานเดียว เมื่อสร้างแล้ว กฏจะสร้างการกําหนดค่าบางอย่าง และเรียกใช้ Sphinx เพื่อสร้างเว็บไซต์ HTML ซึ่งพร้อมสําหรับการตรวจสอบด้วยตนเอง อย่างไรก็ตาม จะมีการสร้างเป้าหมาย "docs.publish" เพิ่มเติมด้วย ซึ่งจะสร้างสคริปต์สำหรับการเผยแพร่เว็บไซต์ เมื่อตรวจสอบเอาต์พุตของเป้าหมายหลักแล้ว คุณจะใช้ bazel run :docs.publish เพื่อเผยแพร่เอาต์พุตดังกล่าวต่อสาธารณะได้ เช่นเดียวกับคำสั่ง bazel publish จินตนาการ

แต่ยังไม่ทราบชัดเจนทันทีว่าการใช้งานกฎ _sphinx_publisher มีลักษณะอย่างไร การดำเนินการเช่นนี้มักจะเขียนสคริปต์เชลล์ Launcher โดยปกติแล้ววิธีนี้เกี่ยวข้องกับการใช้ ctx.actions.expand_template เพื่อเขียนเชลล์สคริปต์อย่างง่าย ซึ่งในกรณีนี้คือการเรียกใช้ไบนารีของผู้เผยแพร่โฆษณาพร้อมเส้นทางไปยังเอาต์พุตของเป้าหมายหลัก วิธีนี้ช่วยให้ผู้เผยแพร่โฆษณาใช้การนําไปใช้แบบทั่วไปได้ กฎ _sphinx_site จะสร้างได้แค่ HTML และสคริปต์เล็กๆ นี้เป็นสิ่งที่จําเป็นทั้งหมดในการรวมทั้งสองเข้าด้วยกัน

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