BazelCon 2022 akan hadir pada 16-17 November ke New York dan online.
Daftar sekarang.

Menggunakan Makro untuk Membuat Kata Kerja Khusus

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.

Interaksi sehari-hari dengan Bazel terjadi terutama melalui beberapa perintah: build, test, dan run. Namun terkadang, batasan ini mungkin terasa terbatas: Anda mungkin perlu mengirimkan paket ke repositori, memublikasikan dokumentasi untuk pengguna akhir, atau men-deploy aplikasi dengan Kubernetes. Namun, Bazel tidak memiliki perintah publish atau deploy. Di mana tindakan ini dapat diterapkan?

Perintah run bazel

Fokus Bazel pada hermeticity, reproduksi, dan inkrementalitas berarti perintah build dan test tidak berguna untuk tugas di atas. Tindakan ini dapat berjalan di sandbox dengan akses jaringan yang terbatas dan tidak dijamin akan dijalankan kembali dengan setiap bazel build.

Sebagai gantinya, andalkan bazel run: pekerja keras untuk tugas yang Anda inginkan memiliki efek samping. Pengguna Bazel sudah terbiasa dengan aturan yang membuat file executable, dan penulis aturan dapat mengikuti sekumpulan pola umum untuk memperluasnya ke "kata kerja kustom".

Dalam alam liar: rules_k8s

Misalnya, pertimbangkan rules_k8s, aturan Kubernetes untuk Bazel. Misalnya Anda memiliki target berikut:

# BUILD file in //application/k8s
k8s_object(
    name = "staging",
    kind = "deployment",
    cluster = "testing",
    template = "deployment.yaml",
)

Aturan k8s_object membuat file YAML Kubernetes standar saat bazel build digunakan pada target staging. Namun, target tambahan juga dibuat oleh makro k8s_object dengan nama seperti staging.apply dan :staging.delete. Skrip build ini untuk menjalankan tindakan tersebut, dan saat dijalankan dengan bazel run staging.apply, skrip ini berperilaku seperti perintah bazel k8s-apply atau bazel k8s-delete sendiri.

Contoh lainnya: ts_api_guardian_test

Pola ini juga dapat dilihat di project Angular. Makro ts_api_guardian_test menghasilkan dua target. Yang pertama adalah target nodejs_test standar yang membandingkan beberapa output yang dihasilkan dengan file "golden" (yaitu, file yang berisi output yang diharapkan). Ini dapat di-build dan dijalankan dengan pemanggilan bazel test normal. Di angular-cli, Anda dapat menjalankan satu target tersebut dengan bazel test //etc/api:angular_devkit_core_api.

Seiring waktu, file emas ini mungkin perlu diperbarui untuk alasan yang sah. Mengupdatenya secara manual akan membosankan dan rentan error, jadi makro ini juga menyediakan target nodejs_binary yang mengupdate file emas, bukan membandingkannya dengan target. Secara efektif, skrip pengujian yang sama dapat ditulis untuk dijalankan dalam mode "verifikasi" atau "terima", berdasarkan cara pemanggilannya. Ini mengikuti pola yang sama yang telah Anda pelajari: tidak ada perintah bazel test-accept native, tetapi efek yang sama dapat dicapai dengan bazel run //etc/api:angular_devkit_core_api.accept.

Pola ini bisa sangat kuat, dan menjadi cukup umum setelah Anda mempelajarinya untuk mengenalinya.

Menyesuaikan aturan Anda sendiri

Makro adalah inti dari pola ini. Makro digunakan seperti aturan, tetapi dapat membuat beberapa target. Biasanya, tim akan membuat target dengan nama yang ditentukan yang menjalankan tindakan build utama: mungkin build tersebut membuat biner normal, image Docker, atau arsip kode sumber. Dalam pola ini, target tambahan dibuat untuk menghasilkan skrip yang menjalankan efek samping berdasarkan output target utama, seperti memublikasikan biner yang dihasilkan atau memperbarui output pengujian yang diharapkan.

Untuk menggambarkan hal ini, gabungkan aturan imajiner yang menghasilkan situs dengan Sphinx dengan makro untuk membuat target tambahan yang memungkinkan pengguna memublikasikannya saat siap. Perhatikan aturan yang sudah ada berikut ini untuk membuat situs dengan Sphinx:

_sphinx_site = rule(
     implementation = _sphinx_impl,
     attrs = {"srcs": attr.label_list(allow_files = [".rst"])},
)

Berikutnya, pertimbangkan aturan seperti berikut, yang membuat skrip yang, saat dijalankan, akan memublikasikan halaman yang dihasilkan:

_sphinx_publisher = rule(
    implementation = _publish_impl,
    attrs = {
        "site": attr.label(),
        "_publisher": attr.label(
            default = "//internal/sphinx:publisher",
            executable = True,
        ),
    },
    executable = True,
)

Terakhir, tentukan makro berikut untuk membuat target untuk kedua aturan di atas:

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)

Dalam file BUILD, gunakan makro seolah-olah hanya membuat target utama:

sphinx_site(
    name = "docs",
    srcs = ["index.md", "providers.md"],
)

Dalam contoh ini, target "dokumen" dibuat, seolah-olah makro adalah aturan Bazel tunggal standar. Saat dibuat, aturan tersebut menghasilkan beberapa konfigurasi dan menjalankan Sphinx untuk menghasilkan situs HTML, yang siap untuk diperiksa secara manual. Namun, target "docs.publish" tambahan juga dibuat, yang membuat skrip untuk memublikasikan situs. Setelah memeriksa output target utama, Anda dapat menggunakan bazel run :docs.publish untuk memublikasikannya untuk konsumsi publik, sama seperti perintah bazel publish imajiner.

Penerapan aturan _sphinx_publisher saat ini tidak langsung terlihat. Sering kali, tindakan seperti ini menulis skrip shell peluncur. Metode ini biasanya melibatkan penggunaan ctx.actions.expand_template untuk menulis skrip shell yang sangat sederhana, dalam hal ini dengan memanggil biner penayang dengan jalur ke output target utama Domain. Dengan cara ini, implementasi penayang dapat tetap generik, aturan _sphinx_site hanya dapat menghasilkan HTML, dan skrip kecil ini saja yang diperlukan untuk menggabungkan keduanya.

Di rules_k8s, inilah yang dilakukan .apply: expand_template menulis skrip Bash yang sangat sederhana, berdasarkan apply.sh.tpl, yang menjalankan kubectl dengan output target utama. Skrip ini kemudian dapat di-build dan dijalankan dengan bazel run :staging.apply, yang secara efektif memberikan perintah k8s-apply untuk target k8s_object.