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 aturan konkret yang dibuat instance-nya.
Alasan Anda tidak boleh menggunakan makro lama (dan harus menggunakan makro Simbolik)
Jika memungkinkan, Anda harus menggunakan makro simbolis.
Makro simbolis
- Mencegah tindakan dari jarak jauh
- Memungkinkan untuk menyembunyikan detail implementasi melalui visibilitas terperinci
- Ambil atribut yang diketik, yang pada akhirnya berarti label otomatis dan konversi yang dipilih.
- Lebih mudah dibaca
- Segera akan 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 di-hardcode 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, sebaiknya
ekstrak kode ini ke fungsi makro. Untuk membuat makro yang disebut
file_generator
, yang memiliki parameter name
dan arg
, kita dapat mengganti
genrule dengan kode 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 berada dalam
paket //path
. Dengan menempatkan definisi fungsi makro dalam file .bzl
terpisah, Anda akan 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
mengaitkan 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 merantai aturan. Contoh ini menunjukkan genrule berantai, 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 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-nya
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 file .bzl
, dan bukan dalam file BUILD
.
Resolusi label dalam makro
Karena makro lama dievaluasi dalam
fase pemuatan, string label seperti
"//foo:bar"
yang terjadi dalam makro lama ditafsirkan secara relatif terhadap
file BUILD
tempat makro digunakan, bukan secara relatif terhadap file .bzl
tempat makro tersebut ditentukan. Perilaku ini umumnya tidak diinginkan untuk makro yang
dimaksudkan untuk digunakan di repositori lain, seperti karena merupakan bagian dari
aturan Starlark yang dipublikasikan.
Untuk mendapatkan perilaku yang sama seperti untuk 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 repo, 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 menampilkan tampilan fileBUILD
setelah evaluasi. Semua makro, glob, loop lama diperluas. Batasan umum: Ekspresiselect
tidak 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 tempat persis aturan
foo
dibuat dalam fileBUILD
, Anda dapat mencoba trik berikut. Masukkan baris ini di dekat bagian atas fileBUILD
:cc_library(name = "foo")
. Jalankan Bazel. Anda akan mendapatkan pengecualian saat aturanfoo
dibuat (karena konflik nama), yang akan menampilkan stack trace lengkap.Anda juga dapat menggunakan print untuk proses debug. Tindakan ini akan menampilkan pesan sebagai baris log
DEBUG
selama fase pemuatan. Kecuali dalam kasus yang jarang terjadi, hapus panggilanprint
, atau buat panggilan tersebut bersyarat berdasarkan parameterdebugging
yang ditetapkan secara default keFalse
sebelum 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 dimulai dengan garis bawah) yang membuat instance aturan harus memiliki argumen
name
. Argumen ini tidak boleh bersifat opsional (jangan berikan nilai default).Fungsi publik harus menggunakan docstring yang mengikuti konvensi Python.
Dalam file
BUILD
, argumenname
makro harus berupa argumen kata kunci (bukan argumen posisional).Atribut
name
aturan yang dihasilkan oleh makro harus menyertakan argumen nama sebagai awalan. Misalnya,macro(name = "foo")
dapat menghasilkancc_library
foo
dan genrulefoo_gen
.Pada umumnya, parameter opsional harus memiliki nilai default
None
.None
dapat diteruskan langsung ke aturan native, yang memperlakukannya sama seperti Anda belum meneruskan argumen apa pun. Dengan demikian, Anda tidak perlu menggantinya dengan0
,False
, atau[]
untuk tujuan ini. Sebagai gantinya, makro harus menunda ke aturan yang dibuatnya, karena default-nya mungkin rumit atau dapat berubah dari waktu ke waktu. Selain itu, parameter yang secara eksplisit ditetapkan 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
visibility
opsional.