Makro lama adalah fungsi tidak terstruktur yang dipanggil dari file BUILD yang dapat membuat target. Pada akhir fase
pemuatan, makro lama tidak ada
lagi, dan Bazel hanya melihat kumpulan konkret aturan yang dibuat.
Alasan Anda tidak boleh menggunakan makro lama (dan harus menggunakan makro Simbolik sebagai gantinya)
Jika memungkinkan, Anda harus menggunakan makro simbolik.
Makro simbolik
- Mencegah tindakan dari jarak jauh
- Memungkinkan menyembunyikan detail implementasi melalui visibilitas terperinci
- Menggunakan atribut yang diketik, yang pada gilirannya berarti konversi label dan pilihan otomatis.
- Lebih mudah dibaca
- Akan segera memiliki evaluasi lambat
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 dikodekan secara permanen dalam perintah:
genrule(
name = "file",
outs = ["file.txt"],
cmd = "$(location //:generator) some_arg > $@",
tools = ["//:generator"],
)
Jika ingin membuat lebih banyak file dengan argumen yang berbeda, Anda mungkin ingin mengekstrak kode ini ke fungsi makro. Untuk membuat makro bernama file_generator, yang memiliki parameter name dan arg, kita dapat mengganti 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 di paket //path. Dengan menempatkan definisi fungsi makro dalam file .bzl terpisah, Anda menjaga file BUILD tetap bersih dan deklaratif. File .bzl dapat dimuat dari paket mana pun di ruang kerja.
Terakhir, di path/generator.bzl, tulis definisi makro untuk merangkum dan memparameterisasi 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 genrule yang digabungkan, dengan genrule menggunakan output genrule sebelumnya sebagai input:
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.
.Memperluas makro
Jika 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 mana yang memanggil
makro), gunakan fungsi
native.package_name(). Perhatikan bahwa
native hanya dapat digunakan dalam .bzl file, dan tidak dalam BUILD file.
Resolusi label dalam makro
Karena makro lama dievaluasi dalam fase pemuatan
, string label seperti
"//foo:bar" yang muncul dalam makro lama ditafsirkan 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 karena merupakan bagian dari ruleset Starlark yang dipublikasikan.
Untuk mendapatkan perilaku yang sama seperti untuk aturan Starlark, gabungkan string label dengan
Label konstruktor:
# @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 repo, such as due to repo mappings.
Label("@dep_of_my_ruleset//tools:foo"),
],
"//conditions:default": [],
}),
**kwargs,
)
Dengan mengaktifkan flag --incompatible_eagerly_resolve_select_keys, semua kunci
yang merupakan string label akan otomatis di-resolve ke objek Label relatif terhadap paket file yang berisi panggilan select. Jika tidak dipilih, gabungkan string label dengan
native.package_relative_label().
Proses debug
bazel query --output=build //my/path:allakan menunjukkan tampilan fileBUILDsetelah evaluasi. Semua makro lama, glob, loop diperluas. Batasan yang diketahui: ekspresiselecttidak ditampilkan dalam output.Anda dapat memfilter output berdasarkan
generator_function(fungsi yang menghasilkan aturan) ataugenerator_name(atribut nama makro):bash $ bazel query --output=build 'attr(generator_function, my_macro, //my/path:all)'Untuk mengetahui lokasi pasti aturan
foodibuat dalam fileBUILD, Anda dapat mencoba trik berikut. Sisipkan baris ini di dekat bagian atasBUILDfile:cc_library(name = "foo"). Jalankan Bazel. Anda akan mendapatkan pengecualian saat aturanfoodibuat (karena konflik nama), yang akan menampilkan pelacakan tumpukan lengkap.Anda juga dapat menggunakan print untuk proses debug. Pesan ini ditampilkan sebagai baris log
DEBUGselama fase pemuatan. Kecuali dalam kasus yang jarang terjadi, hapus panggilanprint, atau buat panggilan tersebut bersyarat di bawah parameterdebuggingyang secara default ditetapkan keFalsesebelum mengirimkan kode ke depot.
Error
Jika Anda ingin menampilkan error, gunakan fungsi fail. Jelaskan dengan jelas kepada pengguna apa yang salah dan cara memperbaiki file BUILD mereka. Error tidak dapat ditangkap.
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 opsional (jangan berikan nilai default).Fungsi publik harus menggunakan docstring yang mengikuti konvensi Python.
Dalam file
BUILD, argumennamemakro harus berupa argumen kata kunci (bukan argumen posisi).Atribut
nameaturan yang dibuat oleh makro harus menyertakan argumen nama sebagai awalan. Misalnya,macro(name = "foo")dapat menghasilkancc_libraryfoodan genrulefoo_gen.Dalam sebagian besar kasus, parameter opsional harus memiliki nilai default
None.Nonedapat diteruskan langsung ke aturan native, yang memperlakukannya sama seperti jika Anda tidak meneruskan argumen apa pun. Oleh karena itu, Anda tidak perlu menggantinya dengan0,False, atau[]untuk tujuan ini. Sebagai gantinya, makro harus menangguhkan aturan yang dibuatnya, karena defaultnya mungkin kompleks atau dapat berubah dari waktu ke waktu. Selain itu, parameter yang ditetapkan secara eksplisit ke nilai defaultnya terlihat berbeda dengan parameter yang tidak pernah ditetapkan (atau ditetapkan keNone) saat diakses melalui bahasa kueri atau internal sistem build.Makro harus memiliki argumen
visibilityopsional.