Halaman ini membahas dua sistem visibilitas Bazel: visibilitas target dan visibilitas pemuatan.
Kedua jenis visibilitas ini membantu developer lain membedakan antara API publik pustaka Anda dan detail implementasinya, serta membantu menerapkan struktur saat ruang kerja Anda berkembang. Anda juga dapat menggunakan visibilitas saat menghentikan penggunaan API publik untuk mengizinkan pengguna saat ini sekaligus menolak pengguna baru.
Visibilitas target
Visibilitas target mengontrol siapa yang dapat bergantung pada target Anda — yaitu, siapa yang dapat
menggunakan label target Anda di dalam atribut seperti deps
. Target akan gagal
dibangun selama fase analisis jika target tersebut
melanggar visibilitas salah satu dependensinya.
Secara umum, target A
dapat dilihat oleh target B
jika keduanya berada di lokasi yang sama, atau jika A
memberikan visibilitas ke lokasi B
. Jika tidak ada makro simbolis, istilah "lokasi" dapat disederhanakan menjadi "paket" saja; lihat di bawah untuk mengetahui informasi selengkapnya tentang makro simbolis.
Visibilitas ditentukan dengan mencantumkan paket yang diizinkan. Mengizinkan paket tidak selalu berarti bahwa subpaketnya juga diizinkan. Untuk mengetahui detail selengkapnya tentang paket dan subpaket, lihat Konsep dan terminologi.
Untuk pembuatan prototipe, Anda dapat menonaktifkan penerapan visibilitas target dengan menyetel
flag --check_visibility=false
. Hal ini tidak boleh dilakukan untuk penggunaan produksi dalam kode yang dikirimkan.
Cara utama untuk mengontrol visibilitas adalah dengan atribut
visibility
aturan.
Subbagian berikut menjelaskan format atribut, cara menerapkannya ke
berbagai jenis target, dan interaksi antara sistem visibilitas dan
makro simbolis.
Spesifikasi visibilitas
Semua target aturan memiliki atribut visibility
yang mengambil daftar label. Setiap
label memiliki salah satu bentuk berikut. Dengan pengecualian formulir terakhir, ini hanyalah
placeholder sintaksis yang tidak sesuai dengan target sebenarnya.
"//visibility:public"
: Memberikan akses ke semua paket."//visibility:private"
: Tidak memberikan akses tambahan apa pun; hanya target dalam paket lokasi ini yang dapat menggunakan target ini."//foo/bar:__pkg__"
: Memberikan akses ke//foo/bar
(tetapi bukan subpaketnya)."//foo/bar:__subpackages__"
: Memberikan akses ke//foo/bar
dan semua sub-paket langsung dan tidak langsungnya."//some_pkg:my_package_group"
: Memberikan akses ke semua paket yang merupakan bagian daripackage_group
tertentu.- Grup paket menggunakan
sintaksis yang berbeda untuk
menentukan paket. Dalam grup paket, bentuk
"//foo/bar:__pkg__"
dan"//foo/bar:__subpackages__"
masing-masing digantikan oleh"//foo/bar"
dan"//foo/bar/..."
. Demikian pula,"//visibility:public"
dan"//visibility:private"
hanyalah"public"
dan"private"
.
- Grup paket menggunakan
sintaksis yang berbeda untuk
menentukan paket. Dalam grup paket, bentuk
Misalnya, jika //some/package:mytarget
memiliki visibility
yang ditetapkan ke
[":__subpackages__", "//tests:__pkg__"]
, maka //some/package:mytarget
dapat digunakan oleh target mana pun
yang merupakan bagian dari hierarki sumber //some/package/...
, serta target
yang dideklarasikan di //tests/BUILD
, tetapi tidak oleh target yang ditentukan di
//tests/integration/BUILD
.
Praktik terbaik: Agar beberapa target terlihat oleh kumpulan paket yang sama, gunakan package_group
, bukan mengulangi daftar di setiap atribut visibility
target. Hal ini meningkatkan keterbacaan dan mencegah daftar menjadi tidak sinkron.
Praktik terbaik: Saat memberikan visibilitas ke project tim lain, sebaiknya gunakan
__subpackages__
daripada __pkg__
untuk menghindari perubahan visibilitas yang tidak perlu saat
project tersebut berkembang dan menambahkan subpaket baru.
Visibilitas target aturan
Visibilitas target aturan ditentukan dengan mengambil atribut visibility
-nya
-- atau default yang sesuai jika tidak diberikan -- dan menambahkan lokasi tempat
target dideklarasikan. Untuk target yang tidak dideklarasikan dalam makro simbolis, jika paket menentukan default_visibility
,
default ini akan digunakan; untuk semua paket lainnya dan untuk target yang dideklarasikan dalam
makro simbolis, defaultnya hanya ["//visibility:private"]
.
# //mypkg/BUILD
package(default_visibility = ["//friend:__pkg__"])
cc_library(
name = "t1",
...
# No visibility explicitly specified.
# Effective visibility is ["//friend:__pkg__", "//mypkg:__pkg__"].
# If no default_visibility were given in package(...), the visibility would
# instead default to ["//visibility:private"], and the effective visibility
# would be ["//mypkg:__pkg__"].
)
cc_library(
name = "t2",
...
visibility = [":clients"],
# Effective visibility is ["//mypkg:clients, "//mypkg:__pkg__"], which will
# expand to ["//another_friend:__subpackages__", "//mypkg:__pkg__"].
)
cc_library(
name = "t3",
...
visibility = ["//visibility:private"],
# Effective visibility is ["//mypkg:__pkg__"]
)
package_group(
name = "clients",
packages = ["//another_friend/..."],
)
Praktik terbaik: Hindari menyetel default_visibility
ke publik. Hal ini mungkin
nyaman untuk pembuatan prototipe atau dalam codebase kecil, tetapi risiko pembuatan target publik secara tidak sengaja meningkat seiring berkembangnya codebase. Lebih baik untuk menyatakan secara eksplisit target mana yang merupakan bagian dari antarmuka publik paket.
Visibilitas target file yang dihasilkan
Target file yang dihasilkan memiliki visibilitas yang sama dengan target aturan yang menghasilkannya.
# //mypkg/BUILD
java_binary(
name = "foo",
...
visibility = ["//friend:__pkg__"],
)
# //friend/BUILD
some_rule(
name = "bar",
deps = [
# Allowed directly by visibility of foo.
"//mypkg:foo",
# Also allowed. The java_binary's "_deploy.jar" implicit output file
# target the same visibility as the rule target itself.
"//mypkg:foo_deploy.jar",
]
...
)
Visibilitas target file sumber
Target file sumber dapat dideklarasikan secara eksplisit menggunakan
exports_files
, atau dibuat secara implisit
dengan merujuk ke nama filenya dalam atribut label aturan (di luar
makro simbolis). Seperti halnya target aturan, lokasi panggilan ke
exports_files
, atau file BUILD yang merujuk ke file input, selalu
ditambahkan secara otomatis ke visibilitas file.
File yang dideklarasikan oleh exports_files
dapat ditetapkan visibilitasnya oleh
parameter visibility
ke fungsi tersebut. Jika parameter ini tidak diberikan, visibilitasnya adalah publik.
Untuk file yang tidak muncul dalam panggilan ke exports_files
, visibilitas
bergantung pada nilai flag
--incompatible_no_implicit_file_export
:
Jika tanda benar, visibilitasnya adalah pribadi.
Jika tidak, perilaku lama akan berlaku: Visibilitasnya sama dengan
BUILD
filedefault_visibility
, atau pribadi jika visibilitas default tidak ditentukan.
Jangan mengandalkan perilaku lama. Selalu tulis deklarasi exports_files
setiap kali target file sumber memerlukan visibilitas non-pribadi.
Praktik terbaik: Jika memungkinkan, lebih baik ekspos target aturan daripada
file sumber. Misalnya, alih-alih memanggil exports_files
pada file .java
,
bungkus file dalam target java_library
non-pribadi. Secara umum, target aturan
hanya boleh mereferensikan secara langsung file sumber yang berada dalam paket yang sama.
Contoh
File //frobber/data/BUILD
:
exports_files(["readme.txt"])
File //frobber/bin/BUILD
:
cc_binary(
name = "my-program",
data = ["//frobber/data:readme.txt"],
)
Visibilitas setelan konfigurasi
Secara historis, Bazel tidak menerapkan visibilitas untuk target config_setting
yang dirujuk dalam kunci select()
. Ada
dua tanda untuk menghapus perilaku lama ini:
--incompatible_enforce_config_setting_visibility
memungkinkan pemeriksaan visibilitas untuk target ini. Untuk membantu migrasi, perubahan ini juga menyebabkanconfig_setting
yang tidak menentukanvisibility
dianggap publik (terlepas daridefault_visibility
tingkat paket).--incompatible_config_setting_private_default_visibility
menyebabkanconfig_setting
yang tidak menentukanvisibility
untuk mematuhidefault_visibility
paket dan melakukan penggantian ke visibilitas pribadi, sama seperti target aturan lainnya. Tidak ada operasi jika--incompatible_enforce_config_setting_visibility
tidak disetel.
Jangan mengandalkan perilaku lama. config_setting
yang dimaksudkan untuk
digunakan di luar paket saat ini harus memiliki visibility
eksplisit, jika
paket belum menentukan default_visibility
yang sesuai.
Visibilitas target grup paket
Target package_group
tidak memiliki atribut visibility
. Informasi ini selalu
dapat dilihat secara publik.
Visibilitas dependensi implisit
Beberapa aturan memiliki dependensi implisit —
dependensi yang tidak dijelaskan dalam file BUILD
, tetapi melekat pada
setiap instance aturan tersebut. Misalnya, aturan cc_library
dapat membuat
dependensi implisit dari setiap target aturannya ke target yang dapat dieksekusi
yang merepresentasikan compiler C++.
Visibilitas dependensi implisit tersebut diperiksa sehubungan dengan
paket yang berisi file .bzl
tempat aturan (atau aspek) ditentukan. Dalam
contoh kita, compiler C++ dapat bersifat pribadi selama berada dalam paket yang sama dengan definisi aturan cc_library
. Sebagai penggantian, jika dependensi implisit tidak terlihat dari definisi, dependensi tersebut diperiksa sehubungan dengan target cc_library
.
Jika Anda ingin membatasi penggunaan aturan pada paket tertentu, gunakan visibilitas pemuatan.
Visibilitas dan makro simbolis
Bagian ini menjelaskan cara sistem visibilitas berinteraksi dengan makro simbolis.
Lokasi dalam makro simbolis
Detail utama sistem visibilitas adalah cara kami menentukan lokasi
deklarasi. Untuk target yang tidak dideklarasikan dalam makro simbolis, lokasi
hanyalah paket tempat target berada -- paket file BUILD
.
Namun, untuk target yang dibuat dalam makro simbolis, lokasinya adalah paket yang berisi file .bzl
tempat definisi makro (pernyataan my_macro = macro(...)
) muncul. Jika target dibuat di dalam
beberapa target bertingkat, definisi makro simbolik paling dalamlah
yang digunakan.
Sistem yang sama digunakan untuk menentukan lokasi mana yang akan diperiksa berdasarkan visibilitas dependensi tertentu. Jika target yang menggunakan dibuat di dalam makro, kita melihat definisi makro paling dalam, bukan paket tempat target yang menggunakan berada.
Artinya, semua makro yang kodenya ditentukan dalam paket yang sama akan otomatis "berinteraksi" satu sama lain. Target apa pun yang dibuat langsung oleh makro
yang ditentukan dalam //lib:defs.bzl
dapat dilihat dari makro lain yang ditentukan dalam //lib
,
terlepas dari paket tempat makro tersebut di-instantiate. Demikian pula, mereka dapat melihat, dan dapat dilihat oleh, target yang dideklarasikan langsung di //lib/BUILD
dan makro lamanya. Sebaliknya, target yang berada dalam paket yang sama tidak
selalu dapat melihat satu sama lain jika setidaknya salah satunya dibuat oleh makro
simbolis.
Dalam fungsi penerapan makro simbolis, parameter visibility
memiliki nilai efektif atribut visibility
makro setelah menambahkan lokasi tempat makro dipanggil. Cara standar agar makro mengekspor
salah satu targetnya ke pemanggilnya adalah dengan meneruskan nilai ini ke deklarasi
target, seperti pada some_rule(..., visibility = visibility)
. Target yang tidak menyertakan
atribut ini tidak akan terlihat oleh pemanggil makro, kecuali jika pemanggil
berada dalam paket yang sama dengan definisi makro. Perilaku ini
menyusun, dalam arti bahwa rangkaian panggilan bertingkat ke submakro masing-masing dapat meneruskan
visibility = visibility
, mengekspor ulang target yang diekspor makro dalam ke
pemanggil di setiap level, tanpa mengekspos detail implementasi makro.
Mendelegasikan hak istimewa ke submakro
Model visibilitas memiliki fitur khusus untuk memungkinkan makro mendelegasikan izinnya ke submakro. Hal ini penting untuk memfaktorkan dan menyusun makro.
Misalkan Anda memiliki makro my_macro
yang membuat tepi dependensi menggunakan aturan
some_library
dari paket lain:
# //macro/defs.bzl
load("//lib:defs.bzl", "some_library")
def _impl(name, visibility, ...):
...
native.genrule(
name = name + "_dependency"
...
)
some_library(
name = name + "_consumer",
deps = [name + "_dependency"],
...
)
my_macro = macro(implementation = _impl, ...)
# //pkg/BUILD
load("//macro:defs.bzl", "my_macro")
my_macro(name = "foo", ...)
Target //pkg:foo_dependency
tidak memiliki visibility
yang ditentukan, sehingga hanya
terlihat dalam //macro
, yang berfungsi dengan baik untuk target yang menggunakan. Sekarang, apa yang terjadi jika penulis //lib
memfaktorkan ulang some_library
untuk diimplementasikan menggunakan makro?
# //lib:defs.bzl
def _impl(name, visibility, deps, ...):
some_rule(
# Main target, exported.
name = name,
visibility = visibility,
deps = deps,
...)
some_library = macro(implementation = _impl, ...)
Dengan perubahan ini, lokasi //pkg:foo_consumer
sekarang adalah //lib
, bukan
//macro
, sehingga penggunaannya atas //pkg:foo_dependency
melanggar visibilitas
dependensi. Penulis my_macro
tidak dapat diharapkan untuk meneruskan
visibility = ["//lib"]
ke deklarasi dependensi hanya untuk mengatasi
detail penerapan ini.
Oleh karena itu, jika dependensi target juga merupakan nilai atribut makro yang mendeklarasikan target, kami akan memeriksa visibilitas dependensi terhadap lokasi makro, bukan lokasi target yang menggunakan.
Dalam contoh ini, untuk memvalidasi apakah //pkg:foo_consumer
dapat melihat
//pkg:foo_dependency
, kita melihat bahwa //pkg:foo_dependency
juga diteruskan sebagai
input ke panggilan ke some_library
di dalam my_macro
, dan sebagai gantinya, kita memeriksa
visibilitas dependensi terhadap lokasi panggilan ini, //macro
.
Proses ini dapat berulang secara rekursif, selama target atau deklarasi makro berada di dalam makro simbolis lain yang mengambil label dependensi di salah satu atribut berjenis labelnya.
Finalisasi
Target yang dideklarasikan dalam finalizer aturan (makro simbolis dengan finalizer = True
),
selain melihat target yang mengikuti aturan visibilitas makro simbolis biasa, juga dapat melihat semua target yang terlihat oleh paket target finalizer.
Dengan kata lain, jika Anda memigrasikan makro lama berbasis native.existing_rules()
ke
finalizer, target yang dideklarasikan oleh finalizer akan tetap dapat melihat
dependensi lamanya.
Target yang dapat diintrospeksi oleh finalizer menggunakan
native.existing_rules()
dapat ditentukan, tetapi tidak dapat digunakan sebagai dependensi dalam
sistem visibilitas. Misalnya, jika target yang ditentukan makro tidak terlihat oleh
paketnya sendiri atau oleh definisi makro finalizer, dan tidak didelegasikan ke
finalizer, finalizer tidak dapat melihat target tersebut. Namun, perlu diperhatikan bahwa makro lama berbasis
native.existing_rules()
juga tidak akan dapat melihat target tersebut.
Visibilitas beban
Visibilitas pemuatan mengontrol apakah file .bzl
dapat dimuat dari file BUILD
atau .bzl
lain di luar paket saat ini.
Dengan cara yang sama seperti visibilitas target melindungi kode sumber yang dienkapsulasi
oleh target, visibilitas pemuatan melindungi logika build yang dienkapsulasi oleh file
.bzl
. Misalnya, penulis file BUILD
mungkin ingin memfaktorkan beberapa deklarasi target
berulang ke dalam makro dalam file .bzl
. Tanpa perlindungan visibilitas pemuatan, mereka mungkin menemukan makro mereka digunakan kembali oleh kolaborator lain di ruang kerja yang sama, sehingga memodifikasi makro akan merusak build tim lain.
Perhatikan bahwa file .bzl
mungkin atau mungkin tidak memiliki target file sumber yang sesuai.
Jika ya, tidak ada jaminan bahwa visibilitas pemuatan dan visibilitas target bertepatan. Artinya, file BUILD
yang sama mungkin dapat memuat file
.bzl
, tetapi tidak mencantumkannya dalam srcs
dari filegroup
,
atau sebaliknya. Hal ini terkadang menyebabkan masalah pada aturan yang ingin menggunakan file .bzl
sebagai kode sumber, seperti untuk pembuatan dokumentasi atau pengujian.
Untuk pembuatan prototipe, Anda dapat menonaktifkan penerapan visibilitas pemuatan dengan menyetel
--check_bzl_visibility=false
. Seperti --check_visibility=false
, hal ini tidak boleh dilakukan untuk kode yang dikirimkan.
Visibilitas pemuatan tersedia mulai Bazel 6.0.
Mendeklarasikan visibilitas pemuatan
Untuk menyetel visibilitas pemuatan file .bzl
, panggil fungsi
visibility()
dari dalam file.
Argumen untuk visibility()
adalah daftar spesifikasi paket, seperti
atribut packages
dari
package_group
. Namun, visibility()
tidak menerima spesifikasi paket negatif.
Panggilan ke visibility()
hanya boleh terjadi satu kali per file, di tingkat teratas (bukan
di dalam fungsi), dan idealnya segera setelah pernyataan load()
.
Tidak seperti visibilitas target, visibilitas pemuatan default selalu publik. File
yang tidak memanggil visibility()
selalu dapat dimuat dari mana saja di
ruang kerja. Sebaiknya tambahkan visibility("private")
ke bagian atas file .bzl
baru yang tidak secara khusus ditujukan untuk digunakan di luar paket.
Contoh
# //mylib/internal_defs.bzl
# Available to subpackages and to mylib's tests.
visibility(["//mylib/...", "//tests/mylib/..."])
def helper(...):
...
# //mylib/rules.bzl
load(":internal_defs.bzl", "helper")
# Set visibility explicitly, even though public is the default.
# Note the [] can be omitted when there's only one entry.
visibility("public")
myrule = rule(
...
)
# //someclient/BUILD
load("//mylib:rules.bzl", "myrule") # ok
load("//mylib:internal_defs.bzl", "helper") # error
...
Praktik visibilitas beban
Bagian ini menjelaskan tips untuk mengelola deklarasi visibilitas pemuatan.
Memfaktorkan visibilitas
Jika beberapa file .bzl
harus memiliki visibilitas yang sama, akan sangat membantu jika Anda
memasukkan spesifikasi paketnya ke dalam daftar umum. Contoh:
# //mylib/internal_defs.bzl
visibility("private")
clients = [
"//foo",
"//bar/baz/...",
...
]
# //mylib/feature_A.bzl
load(":internal_defs.bzl", "clients")
visibility(clients)
...
# //mylib/feature_B.bzl
load(":internal_defs.bzl", "clients")
visibility(clients)
...
Hal ini membantu mencegah kemiringan yang tidak disengaja antara visibilitas berbagai file .bzl
. Hal ini juga lebih mudah dibaca saat daftar clients
besar.
Menyusun visibilitas
Terkadang, file .bzl
mungkin perlu terlihat oleh daftar yang diizinkan yang terdiri dari beberapa daftar yang diizinkan yang lebih kecil. Hal ini analog dengan cara package_group
dapat menggabungkan package_group
lain melalui atribut includes
.
Misalkan Anda menghentikan penggunaan makro yang banyak digunakan. Anda ingin paket hanya dapat dilihat oleh pengguna lama dan paket yang dimiliki oleh tim Anda sendiri. Anda dapat menulis:
# //mylib/macros.bzl
load(":internal_defs.bzl", "our_packages")
load("//some_big_client:defs.bzl", "their_remaining_uses")
# List concatenation. Duplicates are fine.
visibility(our_packages + their_remaining_uses)
Menghapus duplikat dengan grup paket
Tidak seperti visibilitas target, Anda tidak dapat menentukan visibilitas pemuatan dalam hal
package_group
. Jika Anda ingin menggunakan kembali daftar yang diizinkan yang sama untuk visibilitas target dan visibilitas pemuatan, sebaiknya pindahkan daftar spesifikasi paket ke dalam file .bzl, tempat kedua jenis deklarasi dapat merujuknya. Dengan mengacu pada contoh di bagian Memperhitungkan visibilitas di atas, Anda dapat menulis:
# //mylib/BUILD
load(":internal_defs", "clients")
package_group(
name = "my_pkg_grp",
packages = clients,
)
Tindakan ini hanya berfungsi jika daftar tidak berisi spesifikasi paket negatif.
Melindungi simbol individual
Simbol Starlark yang namanya diawali dengan garis bawah tidak dapat dimuat dari
file lain. Hal ini memudahkan Anda membuat simbol pribadi, tetapi tidak memungkinkan Anda membagikan simbol ini dengan serangkaian file tepercaya yang terbatas. Di sisi lain, visibilitas pemuatan memberi Anda kontrol atas paket lain yang dapat melihat .bzl file
Anda, tetapi tidak memungkinkan Anda mencegah simbol non-garis bawah dimuat.
Untungnya, Anda dapat menggabungkan kedua fitur ini untuk mendapatkan kontrol yang lebih baik.
# //mylib/internal_defs.bzl
# Can't be public, because internal_helper shouldn't be exposed to the world.
visibility("private")
# Can't be underscore-prefixed, because this is
# needed by other .bzl files in mylib.
def internal_helper(...):
...
def public_util(...):
...
# //mylib/defs.bzl
load(":internal_defs", "internal_helper", _public_util="public_util")
visibility("public")
# internal_helper, as a loaded symbol, is available for use in this file but
# can't be imported by clients who load this file.
...
# Re-export public_util from this file by assigning it to a global variable.
# We needed to import it under a different name ("_public_util") in order for
# this assignment to be legal.
public_util = _public_util
Lint bzl-visibility Buildifier
Ada lint Buildifier
yang memberikan peringatan jika pengguna memuat file dari direktori bernama internal
atau private
, saat file pengguna itu sendiri tidak berada di bawah induk
direktori tersebut. Lint ini mendahului fitur visibilitas pemuatan dan tidak diperlukan di
ruang kerja tempat file .bzl
mendeklarasikan visibilitas.