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

Makro

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

Halaman ini mencakup dasar-dasar penggunaan makro dan mencakup kasus penggunaan, debug, dan konvensi pada umumnya.

Makro adalah fungsi yang dipanggil dari file BUILD yang dapat membuat instance aturan. Makro umumnya digunakan untuk enkapsulasi dan penggunaan kembali kode dari aturan yang ada dan makro lainnya. Pada akhir fase pemuatan, makro tidak akan ada lagi, dan Bazel hanya melihat kumpulan aturan instance yang konkret.

Penggunaan

Kasus penggunaan umum untuk makro adalah saat Anda ingin menggunakan kembali aturan.

Misalnya, genrule dalam file BUILD menghasilkan file menggunakan//:generator dengan argumen some_arg yang di-hardcode dalam perintah:

genrule(
    name = "file",
    outs = ["file.txt"],
    cmd = "$(location //:generator) some_arg > $@",
    tools = ["//:generator"],
)

Jika ingin menghasilkan lebih banyak file dengan argumen yang berbeda, Anda dapat mengekstrak kode ini ke fungsi makro. Mari kita panggil makro file_generator, yang memiliki parameter name dan arg. Ganti genrule dengan yang berikut:

load("//path:generator.bzl", "file_generator")

file_generator(
    name = "file",
    arg = "some_arg",
)

file_generator(
    name = "file-two",
    arg = "some_arg_two",
)

file_generator(
    name = "file-three",
    arg = "some_arg_three",
)

Di sini, Anda memuat simbol file_generator dari file .bzl yang terletak dalam paket //path. Dengan menempatkan definisi fungsi makro di file .bzl terpisah, Anda dapat menjaga file BUILD tetap bersih dan deklaratif, File .bzl dapat dimuat dari paket apa pun di ruang kerja.

Terakhir, pada path/generator.bzl, tulis definisi makro untuk mengenkapsulasi dan membuat parameter definisi genrule asli:

def file_generator(name, arg, visibility=None):
  native.genrule(
    name = name,
    outs = [name + ".txt"],
    cmd = "$(location //:generator) %s > $@" % arg,
    tools = ["//:generator"],
    visibility = visibility,
  )

Anda juga dapat menggunakan makro untuk menggabungkan aturan. Contoh ini menunjukkan genrulean berantai, di mana genrule menggunakan output genrule sebelumnya sebagai masukan:

def chained_genrules(name, visibility=None):
  native.genrule(
    name = name + "-one",
    outs = [name + ".one"],
    cmd = "$(location :tool-one) $@",
    tools = [":tool-one"],
    visibility = ["//visibility:private"],
  )

  native.genrule(
    name = name + "-two",
    srcs = [name + ".one"],
    outs = [name + ".two"],
    cmd = "$(location :tool-two) $< $@",
    tools = [":tool-two"],
    visibility = visibility,
  )

Contoh ini hanya menetapkan nilai visibilitas ke genrule kedua. Hal ini memungkinkan penulis makro menyembunyikan output aturan perantara agar tidak bergantung pada target lain di ruang kerja.

Makro yang diperluas

Jika Anda ingin menyelidiki fungsi makro, gunakan perintah query dengan --output=build untuk melihat bentuk yang diperluas:

$ bazel query --output=build :file
# /absolute/path/test/ext.bzl:42:3
genrule(
  name = "file",
  tools = ["//:generator"],
  outs = ["//test:file.txt"],
  cmd = "$(location //:generator) some_arg > $@",
)

Membuat instance aturan native

Aturan native (aturan yang tidak memerlukan pernyataan load()) dapat dibuat instance dari modul native:

def my_macro(name, visibility=None):
  native.cc_library(
    name = name,
    srcs = ["main.cc"],
    visibility = visibility,
  )

Jika Anda perlu mengetahui nama paket (misalnya, file BUILD yang memanggil makro), gunakan fungsi native.package_name(). Perhatikan bahwa native hanya dapat digunakan dalam file .bzl, bukan dalam file WORKSPACE atau BUILD.

Resolusi label dalam makro

Karena makro dievaluasi dalam fase pemuatan, string label seperti "//foo:bar" yang terjadi dalam makro ditafsirkan secara relatif terhadap file BUILD tempat makro digunakan, bukan relatif terhadap file .bzl tempat makro ditentukan. Perilaku ini umumnya tidak diinginkan untuk makro yang dimaksudkan untuk digunakan di repositori lain, seperti makro yang merupakan bagian dari kumpulan aturan Starlark yang dipublikasikan.

Untuk mendapatkan perilaku yang sama seperti aturan Starlark, gabungkan string label dengan konstruktor Label:

# @my_ruleset//rules:defs.bzl
def my_cc_wrapper(name, deps = [], **kwargs):
  native.cc_library(
    name = name,
    deps = deps + select({
      # Due to the use of Label, this label is resolved within @my_ruleset,
      # regardless of its site of use.
      Label("//config:needs_foo"): [
        # Due to the use of Label, this label will resolve to the correct target
        # even if the canonical name of @dep_of_my_ruleset should be different
        # in the main workspace, such as due to repo mappings.
        Label("@dep_of_my_ruleset//tools:foo"),
      ],
      "//conditions:default": [],
    }),
    **kwargs,
  )

Proses Debug

  • bazel query --output=build //my/path:all akan menunjukkan tampilan file BUILD setelah evaluasi. Semua makro, glob, loop diperluas. Batasan yang diketahui: Ekspresi select saat ini tidak ditampilkan dalam output.

  • Anda dapat memfilter output berdasarkan generator_function (fungsi yang menghasilkan aturan) atau generator_name (atribut nama makro): bash $ bazel query --output=build 'attr(generator_function, my_macro, //my/path:all)'

  • Untuk mengetahui tempat aturan foo dibuat dalam file BUILD, Anda dapat mencoba trik berikut. Sisipkan baris ini di dekat bagian atas file BUILD: cc_library(name = "foo"). Jalankan Bazel. Anda akan mendapatkan pengecualian saat aturan foo dibuat (karena konflik nama), yang akan menampilkan pelacakan tumpukan lengkap kepada Anda.

  • Anda juga dapat menggunakan print untuk proses debug. Fitur ini menampilkan pesan sebagai baris log DEBUG selama fase pemuatan. Kecuali dalam kasus yang jarang terjadi, hapus panggilan print atau jadikan sebagai kondisional berdasarkan parameter debugging yang secara default ditetapkan ke False sebelum mengirimkan kode ke depot.

Error

Jika Anda ingin menampilkan error, gunakan fungsi fail. Jelaskan secara jelas kepada pengguna apa yang salah dan cara memperbaiki file BUILD mereka. Anda tidak dapat menemukan error.

def my_macro(name, deps, visibility=None):
  if len(deps) < 2:
    fail("Expected at least two values in deps")
  # ...

Konvensi

  • Semua fungsi publik (fungsi yang tidak diawali dengan garis bawah) yang membuat instance aturan harus memiliki argumen name. Argumen ini tidak boleh bersifat opsional (jangan gunakan nilai default).

  • Fungsi publik harus menggunakan docstring mengikuti konvensi Python.

  • Dalam file BUILD, argumen name dari makro harus berupa argumen kata kunci (bukan argumen posisi).

  • Atribut aturan name yang dihasilkan oleh makro harus menyertakan argumen nama sebagai awalan. Misalnya, macro(name = "foo") dapat menghasilkan cc_library foo dan genrule foo_gen.

  • Biasanya, parameter opsional harus memiliki nilai default None. None dapat diteruskan langsung ke aturan native, yang memperlakukannya sama seperti jika Anda tidak meneruskan argumen apa pun. Dengan demikian, Anda tidak perlu menggantinya dengan 0, False, atau [] untuk tujuan ini. Sebagai gantinya, makro harus mematuhi aturan yang dibuatnya, karena defaultnya mungkin rumit atau dapat berubah seiring. Selain itu, parameter yang secara eksplisit ditetapkan ke nilai default terlihat berbeda dari yang tidak pernah ditetapkan (atau ditetapkan ke None) saat diakses melalui bahasa kueri atau internal sistem build.

  • Makro harus memiliki argumen visibility opsional.