Visibilitas

Laporkan masalah Lihat sumber

Halaman ini membahas dua sistem visibilitas Bazel: visibilitas target dan visibilitas pemuatan.

Kedua jenis visibilitas ini membantu developer lain membedakan antara API publik library Anda dan detail penerapannya, serta membantu menerapkan struktur seiring dengan berkembangnya ruang kerja Anda. Anda juga dapat menggunakan visibilitas saat menghentikan API publik untuk mengizinkan pengguna saat ini sekaligus menolak API baru.

Visibilitas target

Visibilitas target mengontrol siapa yang mungkin bergantung pada target Anda — yaitu, siapa yang dapat menggunakan label target di dalam atribut seperti deps.

A target terlihat oleh B target jika berada dalam paket yang sama, atau jika A memberikan visibilitas ke paket B. Jadi, paket adalah unit perincian untuk memutuskan mengizinkan akses atau tidak. Jika B bergantung pada A, tetapi A tidak terlihat oleh B, setiap upaya untuk membangun B akan gagal selama analisis.

Perhatikan bahwa memberikan visibilitas ke paket tidak dengan sendirinya memberikan visibilitas ke sub-paketnya. Untuk mengetahui detail selengkapnya tentang paket dan sub-paket, 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 pada target aturan. Bagian ini menjelaskan format atribut ini dan cara menentukan visibilitas target.

Spesifikasi visibilitas

Semua target aturan memiliki atribut visibility yang menggunakan daftar label. Setiap label memiliki salah satu bentuk berikut. Dengan pengecualian bentuk terakhir, ini hanya placeholder sintaksis yang tidak sesuai dengan target sebenarnya.

  • "//visibility:public": Memberikan akses ke semua paket. (Tidak dapat digabungkan dengan spesifikasi lainnya.)

  • "//visibility:private": Tidak memberikan akses tambahan; hanya target dalam paket ini yang dapat menggunakan target ini. (Dapat tidak digabungkan dengan spesifikasi lainnya.)

  • "//foo/bar:__pkg__": Memberikan akses ke //foo/bar (tetapi bukan subpaketnya).

  • "//foo/bar:__subpackages__": Memberikan akses //foo/bar dan semua subpaket langsung dan tidak langsungnya.

  • "//some_pkg:my_package_group": Memberikan akses ke semua paket yang merupakan bagian dari package_group yang diberikan.

    • Grup paket menggunakan sintaksis yang berbeda untuk menentukan paket. Dalam grup paket, formulir "//foo/bar:__pkg__" dan "//foo/bar:__subpackages__" masing-masing diganti dengan "//foo/bar" dan "//foo/bar/...". Demikian juga, "//visibility:public" dan "//visibility:private" hanya "public" dan "private".

Misalnya, jika visibility ditetapkan ke [":__subpackages__", "//tests:__pkg__"], //some/package:mytarget dapat digunakan oleh target apa pun yang merupakan bagian dari hierarki sumber //some/package/..., serta target yang ditentukan dalam //tests/BUILD, tetapi tidak oleh target yang ditentukan dalam //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 tidak sinkron.

Visibilitas target aturan

Visibilitas target aturan:

  1. Nilai atribut visibility-nya, jika ditetapkan; atau lainnya

  2. Nilai argumen default_visibility dari pernyataan package dalam file BUILD target, jika deklarasi tersebut ada; atau

  3. //visibility:private.

Praktik terbaik: Hindari menyetel default_visibility ke publik. Mungkin tidak praktis untuk pembuatan prototipe atau dalam codebase kecil, tetapi risiko pembuatan target publik secara tidak sengaja akan meningkat seiring pertumbuhan codebase. Sebaiknya jelaskan target mana yang merupakan bagian dari antarmuka publik paket.

Contoh

File //frobber/bin/BUILD:

# This target is visible to everyone
cc_binary(
    name = "executable",
    visibility = ["//visibility:public"],
    deps = [":library"],
)

# This target is visible only to targets declared in the same package
cc_library(
    name = "library",
    # No visibility -- defaults to private since no
    # package(default_visibility = ...) was used.
)

# This target is visible to targets in package //object and //noun
cc_library(
    name = "subject",
    visibility = [
        "//noun:__pkg__",
        "//object:__pkg__",
    ],
)

# See package group "//frobber:friends" (below) for who can
# access this target.
cc_library(
    name = "thingy",
    visibility = ["//frobber:friends"],
)

File //frobber/BUILD:

# This is the package group declaration to which target
# //frobber/bin:thingy refers.
#
# Our friends are packages //frobber, //fribber and any
# subpackage of //fribber.
package_group(
    name = "friends",
    packages = [
        "//fribber/...",
        "//frobber",
    ],
)

Visibilitas target file yang dihasilkan

Target file yang dihasilkan memiliki visibilitas yang sama dengan target aturan yang membuatnya.

Visibilitas target file sumber

Anda dapat menetapkan visibilitas target file sumber secara eksplisit dengan memanggil exports_files. Jika tidak ada argumen visibility yang diteruskan ke exports_files, visibilitas akan menjadi publik. exports_files tidak boleh digunakan untuk mengganti visibilitas file yang dihasilkan.

Untuk target file sumber yang tidak muncul dalam panggilan ke exports_files, visibilitas bergantung pada nilai flag --incompatible_no_implicit_file_export:

  • Jika tanda ini disetel, visibilitas akan disetel ke pribadi.

  • Selain itu, perilaku lama akan berlaku: Visibilitasnya sama dengan default_visibility file BUILD, atau pribadi jika visibilitas default tidak ditentukan.

Hindari mengandalkan perilaku lama. Selalu tulis deklarasi exports_files setiap kali target file sumber memerlukan visibilitas non-pribadi.

Praktik terbaik: Jika memungkinkan, sebaiknya tampilkan target aturan, bukan file sumber. Misalnya, alih-alih memanggil exports_files pada file .java, gabungkan file dalam target java_library non pribadi. Umumnya, target aturan hanya boleh secara langsung mereferensikan 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 belum menerapkan visibilitas untuk target config_setting yang direferensikan dalam kunci select(). Ada dua tanda untuk menghapus perilaku lama ini:

  • --incompatible_enforce_config_setting_visibility mengaktifkan pemeriksaan visibilitas untuk target ini. Untuk membantu migrasi, hal ini juga menyebabkan semua config_setting yang tidak menentukan visibility dianggap sebagai publik (terlepas dari default_visibility level paket).

  • --incompatible_config_setting_private_default_visibility menyebabkan config_setting yang tidak menentukan visibility untuk mematuhi default_visibility paket dan melakukan penggantian pada visibilitas pribadi, seperti target aturan lainnya. Tidak ada pengoperasian jika --incompatible_enforce_config_setting_visibility tidak ditetapkan.

Hindari mengandalkan perilaku lama. Setiap 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. ID tersebut selalu terlihat oleh publik.

Visibilitas dependensi implisit

Beberapa aturan memiliki dependensi implisit — dependensi yang tidak tereja 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 mewakili compiler C++.

Visibilitas dependensi implisit tersebut diperiksa sehubungan dengan paket yang berisi file .bzl tempat aturan (atau aspek) ditentukan. Dalam contoh kami, 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 akan diperiksa terkait dengan target cc_library.

Anda dapat mengubah perilaku ini dengan menonaktifkan --incompatible_visibility_private_attributes_at_definition. Jika dinonaktifkan, dependensi implisit diperlakukan seperti dependensi lainnya. Artinya, target yang menjadi dependensi (seperti compiler C++) harus terlihat oleh setiap instance aturan. Dalam praktiknya, hal ini biasanya berarti target harus memiliki visibilitas publik.

Jika Anda ingin membatasi penggunaan aturan untuk paket tertentu, gunakan visibilitas beban.

Muat visibilitas

Visibilitas pemuatan mengontrol apakah file .bzl dapat dimuat dari file BUILD atau .bzl lainnya.

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 memperhitungkan beberapa definisi target berulang ke dalam makro dalam file .bzl. Tanpa perlindungan visibilitas beban, mereka mungkin mendapati 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 memiliki atau tidak memiliki target file sumber yang sesuai. Jika ya, tidak ada jaminan bahwa visibilitas beban dan visibilitas target akan sesuai. Artinya, file BUILD yang sama mungkin dapat memuat file .bzl tetapi tidak dapat mencantumkannya dalam srcs dari filegroup, atau sebaliknya. Hal ini terkadang dapat menyebabkan masalah untuk aturan yang ingin menggunakan file .bzl sebagai kode sumber, seperti untuk pembuatan atau pengujian dokumentasi.

Untuk pembuatan prototipe, Anda dapat menonaktifkan penerapan visibilitas beban dengan menyetel --check_bzl_visibility=false. Seperti halnya --check_visibility=false, hal ini tidak boleh dilakukan untuk kode yang dikirimkan.

Visibilitas pemuatan tersedia mulai Bazel 6.0.

Mendeklarasikan visibilitas pemuatan

Untuk menetapkan 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 sekali per file, di tingkat teratas (bukan di dalam fungsi), dan idealnya segera setelah pernyataan load().

Tidak seperti visibilitas target, visibilitas pemuatan default selalu bersifat 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 dimaksudkan 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

...

Memuat praktik visibilitas

Bagian ini menjelaskan tips untuk mengelola deklarasi visibilitas pemuatan.

Mempertimbangkan visibilitas

Jika beberapa file .bzl harus memiliki visibilitas yang sama, sebaiknya faktorkan 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 distorsi yang tidak disengaja di antara berbagai visibilitas file .bzl. Format ini juga lebih mudah dibaca jika daftar clients berukuran besar.

Menyusun visibilitas

Terkadang, file .bzl mungkin harus terlihat oleh daftar yang diizinkan yang terdiri dari beberapa daftar yang diizinkan dengan jumlah lebih kecil. Hal ini setara dengan cara package_group dapat menggabungkan package_group lain melalui atribut includes.

Misalkan Anda menghentikan penggunaan makro yang banyak digunakan. Anda ingin instance tersebut hanya terlihat oleh pengguna yang ada 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)

Menduplikasi grup paket

Tidak seperti visibilitas target, Anda tidak dapat menentukan visibilitas beban 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 file .bzl, tempat kedua jenis deklarasi dapat merujuk ke sana. Berdasarkan contoh dalam Faktoring visibilitas di atas, Anda dapat menulis:

# //mylib/BUILD

load(":internal_defs", "clients")

package_group(
    name = "my_pkg_grp",
    packages = clients,
)

Metode ini hanya berfungsi jika daftar tidak berisi spesifikasi paket negatif.

Melindungi setiap simbol

Setiap simbol Starlark yang namanya diawali dengan garis bawah tidak dapat dimuat dari file lain. Hal ini memudahkan pembuatan simbol pribadi, tetapi tidak memungkinkan Anda membagikan simbol-simbol tersebut kepada sekumpulan file tepercaya yang terbatas. Di sisi lain, visibilitas beban memberi Anda kontrol atas apa yang mungkin dilihat oleh paket lain .bzl file, tetapi tidak memungkinkan Anda mencegah simbol yang tidak digarisbawahi dimuat.

Untungnya, Anda dapat menggabungkan kedua fitur ini untuk mendapatkan kontrol yang lebih detail.

# //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 Buildifier bzl-visibilitas

Ada lint Buildifier yang memberikan peringatan jika pengguna memuat file dari direktori bernama internal atau private, saat file pengguna tidak berada di bawah induk direktori tersebut. Lint ini mendahului fitur visibilitas pemuatan dan tidak diperlukan di ruang kerja tempat file .bzl mendeklarasikan visibilitas.