Visibilitas

Laporkan masalah Lihat sumber Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

Kedua jenis visibilitas ini membantu developer lain membedakan antara API publik library dan detail implementasinya, serta membantu menerapkan struktur seiring berkembangnya ruang kerja Anda. 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 di-build selama fase analisis jika melanggar visibilitas salah satu dependensinya.

Umumnya, target A terlihat oleh target B jika berada di lokasi yang sama, atau jika A memberikan visibilitas ke lokasi B. Jika tidak ada makro simbolis, istilah "lokasi" dapat disederhanakan menjadi hanya "paket"; 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 membuat prototipe, Anda dapat menonaktifkan penerapan visibilitas target dengan menetapkan tanda --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 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.

  • "//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 //foo/bar dan semua sub-paket 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, bentuk "//foo/bar:__pkg__" dan "//foo/bar:__subpackages__" masing-masing diganti dengan "//foo/bar" dan "//foo/bar/...". Demikian pula, "//visibility:public" dan "//visibility:private" hanyalah "public" dan "private".

Misalnya, jika //some/package:mytarget memiliki visibility yang 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 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 tidak sinkron.

Praktik terbaik: Saat memberikan visibilitas ke project tim lain, pilih __subpackages__, bukan __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 lain 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 menetapkan default_visibility ke publik. Hal ini mungkin nyaman untuk membuat prototipe atau dalam codebase kecil, tetapi risiko pembuatan target publik secara tidak sengaja akan meningkat seiring berkembangnya codebase. Sebaiknya jelaskan 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 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 memiliki visibilitas yang ditetapkan 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 tandanya benar, visibilitas bersifat pribadi.

  • Jika tidak, perilaku lama akan berlaku: Visibilitas sama dengan default_visibility file BUILD, atau bersifat 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 ekspos target aturan, bukan file sumber. Misalnya, daripada memanggil exports_files pada file .java, gabungkan file dalam target java_library non-pribadi. Umumnya, target aturan hanya boleh mereferensikan file sumber yang berada dalam paket yang sama secara langsung.

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 memungkinkan pemeriksaan visibilitas untuk target ini. Untuk membantu migrasi, hal ini juga menyebabkan config_setting yang tidak menentukan visibility dianggap publik (terlepas dari default_visibility tingkat paket).

  • --incompatible_config_setting_private_default_visibility menyebabkan config_setting yang tidak menentukan visibility untuk mematuhi default_visibility paket dan kembali ke visibilitas pribadi, seperti target aturan lainnya. Tidak ada operasi 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. Halaman tersebut selalu terlihat secara publik.

Visibilitas dependensi implisit

Beberapa aturan memiliki dependensi implisit — dependensi yang tidak disebutkan 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 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 akan diperiksa sehubungan dengan target cc_library.

Jika Anda ingin membatasi penggunaan aturan ke 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, lokasinya hanya berupa 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. Saat target dibuat di dalam beberapa target bertingkat, target tersebut selalu menggunakan definisi makro simbolis paling dalam yang digunakan.

Sistem yang sama digunakan untuk menentukan lokasi yang akan diperiksa berdasarkan visibilitas dependensi tertentu. Jika target penggunaan dibuat di dalam makro, kita akan melihat definisi makro terdalam, bukan paket tempat target penggunaan berada.

Artinya, semua makro yang kodenya ditentukan dalam paket yang sama akan otomatis menjadi "teman" satu sama lain. Setiap target yang dibuat langsung oleh makro yang ditentukan di //lib:defs.bzl dapat dilihat dari makro lain yang ditentukan di //lib, terlepas dari paket tempat makro tersebut dibuat instance-nya. Demikian pula, target dapat melihat, dan dapat dilihat oleh, target yang dideklarasikan langsung di //lib/BUILD dan makro lama. Sebaliknya, target yang berada dalam paket yang sama tidak selalu dapat melihat satu sama lain jika setidaknya salah satunya dibuat oleh makro simbolik.

Dalam fungsi implementasi makro simbolis, parameter visibility memiliki nilai efektif atribut visibility makro setelah menambahkan lokasi tempat makro dipanggil. Cara standar bagi makro untuk mengekspor salah satu targetnya ke pemanggilnya adalah dengan meneruskan nilai ini ke deklarasi target, seperti dalam some_rule(..., visibility = visibility). Target yang menghilangkan atribut ini tidak akan terlihat oleh pemanggil makro, kecuali jika pemanggil tersebut berada dalam paket yang sama dengan definisi makro. Perilaku ini dikomposisi, dalam arti bahwa rantai panggilan bertingkat ke submakro masing-masing dapat meneruskan visibility = visibility, mengekspor ulang target yang diekspor makro bagian dalam ke pemanggil di setiap tingkat, tanpa mengekspos detail penerapan makro.

Mendelegasikan hak istimewa ke submakro

Model visibilitas memiliki fitur khusus untuk memungkinkan makro mendelegasikan izinnya ke submakro. Hal ini penting untuk faktorisasi dan penyusunan 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 penggunaan. 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 kini menjadi //lib, bukan //macro, sehingga penggunaan //pkg:foo_dependency-nya melanggar visibilitas dependensi. Penulis my_macro tidak dapat diharapkan untuk meneruskan visibility = ["//lib"] ke deklarasi dependensi hanya untuk mengatasi detail implementasi ini.

Karena alasan ini, saat dependensi target juga merupakan nilai atribut makro yang mendeklarasikan target, kita 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 memeriksa visibilitas dependensi terhadap lokasi panggilan ini, //macro.

Proses ini dapat diulang secara rekursif, selama deklarasi target atau makro berada di dalam makro simbolis lain yang menggunakan label dependensi di salah satu atribut berjenis labelnya.

Memuat visibilitas

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 memasukkan beberapa deklarasi target berulang ke dalam makro dalam file .bzl. Tanpa perlindungan visibilitas beban, mereka mungkin menemukan makro mereka digunakan kembali oleh kolaborator lain di ruang kerja yang sama, sehingga mengubah makro akan merusak build tim lain.

Perhatikan bahwa file .bzl mungkin memiliki atau tidak memiliki target file sumber yang sesuai. Jika demikian, tidak ada jaminan bahwa visibilitas beban dan visibilitas target akan sama. Artinya, file BUILD yang sama mungkin dapat memuat file .bzl, tetapi tidak mencantumkannya dalam srcs dari filegroup, atau sebaliknya. Hal ini terkadang dapat menyebabkan masalah bagi aturan yang ingin menggunakan file .bzl sebagai kode sumber, seperti untuk pembuatan atau pengujian dokumentasi.

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

Visibilitas beban 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 atas (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 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 pemuatan

Bagian ini menjelaskan tips untuk mengelola deklarasi visibilitas pemuatan.

Memfaktorkan visibilitas

Jika beberapa file .bzl harus memiliki visibilitas yang sama, sebaiknya masukkan 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 penyimpangan yang tidak disengaja antara berbagai visibilitas file .bzl. Ini juga lebih mudah dibaca jika daftar clients besar.

Mengomposisi 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-nya.

Misalnya, Anda menghentikan penggunaan makro yang banyak digunakan. Anda ingin paket tersebut hanya dapat dilihat 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)

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 file .bzl, tempat kedua jenis deklarasi dapat merujuk kepadanya. Berdasarkan contoh di Faktor visibilitas di atas, Anda dapat menulis:

# //mylib/BUILD

load(":internal_defs", "clients")

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

Hal 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 ini dengan sekumpulan file tepercaya yang terbatas. Di sisi lain, visibilitas pemuatan memberi Anda kontrol atas apa yang dapat dilihat paket lain terhadap .bzl file Anda, tetapi tidak memungkinkan Anda mencegah simbol tanpa garis bawah dimuat.

Untungnya, Anda dapat menggabungkan kedua fitur ini untuk mendapatkan kontrol yang sangat 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 Buildifier bzl-visibility

Ada Buildifier lint 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. Linting ini sudah ada sebelum fitur visibilitas pemuatan dan tidak diperlukan di ruang kerja tempat file .bzl mendeklarasikan visibilitas.