這個頁面說明 Bazel 的兩種瀏覽權限系統:目標瀏覽權限和負載瀏覽權限。
這兩種瀏覽權限都能協助其他開發人員區別程式庫的公用 API 及其實作詳細資料,並可在工作區增加時強制執行結構。淘汰公用 API 時,您也可以使用瀏覽權限,允許現有使用者同時拒絕新的 API。
目標瀏覽權限
「目標瀏覽權限」會控制哪些使用者可能會與您的目標有關,也就是誰可能會在 deps
等屬性中使用目標的標籤。
如果目標 A
位於相同套件中,或者 A
授予 B
套件的瀏覽權限,則該目標 B
可以查看。因此,套件是決定是否允許存取的精細單位。如果 B
依附於 A
,但 B
無法看到 A
,則任何嘗試建構 B
的作業都會在分析期間失敗。
請注意,授予套件瀏覽權限時,不只會授予子套件的瀏覽權限。如要進一步瞭解套件和子套件,請參閱概念和術語一文。
在原型設計中,您可以透過設定 --check_visibility=false
標記,停用目標瀏覽權限強制執行設定。請勿在提交的程式碼中使用這項做法。
控管瀏覽權限的主要方式是使用規則目標的 visibility
屬性。本節說明這個屬性的格式,以及如何確定目標的瀏覽權限。
瀏覽權限規格
所有規則目標都含有 visibility
屬性,該屬性會取得標籤清單。每個標籤都有下列其中一種形式。除了上一份表單以外,這些只是語法預留位置,不會對應到任何實際目標。
"//visibility:public"
:授予所有套件的存取權。(不得與任何其他規格合併使用)。"//visibility:private"
:不會授予任何額外存取權;只有這個套件中的目標可以使用這個目標。(不得與任何其他規格合併使用)。"//foo/bar:__pkg__"
:授予//foo/bar
(而非子套件) 的存取權。"//foo/bar:__subpackages__"
:授予//foo/bar
及其所有直接和間接子套件的存取權。"//some_pkg:my_package_group"
:授予特定package_group
中所有套件的存取權。- 套件群組會使用不同的語法指定套件。在套件群組中,表單
"//foo/bar:__pkg__"
和"//foo/bar:__subpackages__"
會分別由"//foo/bar"
和"//foo/bar/..."
取代。同樣地,"//visibility:public"
和"//visibility:private"
只是"public"
和"private"
。
- 套件群組會使用不同的語法指定套件。在套件群組中,表單
舉例來說,如果 //some/package:mytarget
的 visibility
設為 [":__subpackages__", "//tests:__pkg__"]
,就可用於屬於 //some/package/...
來源樹狀結構的任何目標,以及在 //tests/BUILD
中定義的目標,但不適用於 //tests/integration/BUILD
中定義的目標。
最佳做法:如要讓同一套件組合顯示數個目標,請使用 package_group
,而非在每個目標的 visibility
屬性中重複清單。這可以提高可讀性,避免清單無法同步。
規則目標瀏覽權限
規則目標的瀏覽權限如下:
其
visibility
屬性的值 (如有設定);或者也可採用其他值在目標
BUILD
檔案中的package
陳述式的default_visibility
引數值 (如有這類宣告);或者,//visibility:private
.
最佳做法:避免將 default_visibility
設為公開。這可能就適合進行原型設計或小型程式碼集,但隨著程式碼集規模的增加,您無意間建立公開目標的風險也會增加。建議您明確指出哪些目標屬於套件公開介面的一部分。
範例
檔案 //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"],
)
檔案 //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",
],
)
產生的檔案目標瀏覽權限
產生的檔案目標的瀏覽權限與產生該檔案的規則目標相同。
來源檔案目標瀏覽權限
您可以呼叫 exports_files
來明確設定來源檔案目標的瀏覽權限。沒有將 visibility
引數傳遞至 exports_files
時,系統會公開瀏覽權限。exports_files
可能無法用於覆寫所產生檔案的瀏覽權限。
針對未顯示在 exports_files
呼叫中的來源檔案目標,瀏覽權限取決於標記 --incompatible_no_implicit_file_export
的值:
如果設定旗標,瀏覽權限即為私人。
否則,系統會套用舊版行為:瀏覽權限與
BUILD
檔案的default_visibility
相同;如未指定預設瀏覽權限,則為不公開。
請避免採用舊版行為。當來源檔案目標需要非私人的瀏覽權限時,請一律編寫 exports_files
宣告。
最佳做法:請盡可能公開規則目標,而非來源檔案。舉例來說,請將檔案納入非私人的 java_library
目標中,而不是對 .java
檔案呼叫 exports_files
。一般來說,規則目標應只直接參照位於相同套件中的來源檔案。
範例
檔案 //frobber/data/BUILD
:
exports_files(["readme.txt"])
檔案 //frobber/bin/BUILD
:
cc_binary(
name = "my-program",
data = ["//frobber/data:readme.txt"],
)
配置設定瀏覽權限
過去,Bazel 並未針對 select()
索引鍵中參照的 config_setting
目標強制執行瀏覽權限。如要移除這個舊版行為,有兩個標記:
--incompatible_enforce_config_setting_visibility
可針對這些目標進行瀏覽權限檢查。為協助進行遷移,系統也會將任何未指定visibility
的config_setting
視為公開 (無論套件層級的default_visibility
為何)。--incompatible_config_setting_private_default_visibility
會讓config_setting
未指定visibility
,以遵循套件的default_visibility
,並在不公開瀏覽權限方面備用,就像任何其他規則目標一樣。如未設定--incompatible_enforce_config_setting_visibility
,則免人工管理。
請避免採用舊版行為。如果套件尚未指定合適的 default_visibility
,任何打算在現有套件外使用的 config_setting
都應具有明確的 visibility
。
套件群組目標瀏覽權限
package_group
個目標沒有 visibility
屬性。而是一律公開顯示
隱含依附元件的瀏覽權限
部分規則具有隱含依附元件,也就是未在 BUILD
檔案中排除,但基本上可套用至該規則的每個執行個體的依附元件。舉例來說,cc_library
規則可能會從每個規則目標,建立代表 C++ 編譯器的可執行目標,以建立隱含依附元件。
目前,為了掌握瀏覽權限,這些隱含依附元件被視為其他依附元件。這表示規則的每個執行個體都必須能看見依附的目標 (例如 C++ 編譯器)。在實務中,通常表示目標必須具有公開瀏覽權限。
您可以設定 --incompatible_visibility_private_attributes_at_definition
來變更這項行為。啟用後,只有宣告隱含依附元件的規則可以看見有問題的目標。也就是說,您必須能在包含定義規則的 .bzl
檔案的套件中看到它。在我們的範例中,只要 C++ 編譯器與 cc_library
規則定義位於相同的套件中,就可以保持不公開。
載入瀏覽權限
「載入瀏覽權限」可控管系統是否從其他 BUILD
或 .bzl
檔案載入 .bzl
檔案。
與目標瀏覽權限保護的原始碼一樣,負載瀏覽權限保護由 .bzl
檔案封裝的建構邏輯。舉例來說,BUILD
檔案作者可能會想將某些重複目標定義簡化為 .bzl
檔案中的巨集。如果沒有保護載入瀏覽權限,他們可能會發現同一工作區中的其他協作者重複使用自己的巨集,因而導致修改巨集會破壞其他團隊的建構。
請注意,.bzl
檔案不一定有對應的來源檔案目標。如果是這樣,我們無法保證載入的瀏覽權限和目標可見度相符。也就是說,相同的 BUILD
檔案或許可以載入 .bzl
檔案,但無法在 filegroup
的 srcs
中列出,反之亦然。假如規則想要將 .bzl
檔案做為原始碼使用 (例如產生或測試文件),這有時可能會導致問題。
在原型設計中,您可以設定 --check_bzl_visibility=false
,藉此停用載入瀏覽權限強制執行功能。和 --check_visibility=false
一樣,請勿為提交的程式碼完成這項操作。
從 Bazel 6.0 開始,即可使用負載瀏覽權限。
宣告負載瀏覽權限
如要設定 .bzl
檔案的載入瀏覽權限,請從檔案呼叫 visibility()
函式。visibility()
的引數是套件規格的清單,就像 package_group
的 packages
屬性一樣。不過,visibility()
不接受排除的套件規格。
對 visibility()
發出的呼叫只能在每個檔案的頂層 (而非函式中) 發生一次,最好緊接在 load()
陳述式之後。
與目標瀏覽權限不同的是,預設的載入瀏覽權限一律為公開。如果檔案未呼叫 visibility()
,可以隨時從工作區的任何位置載入。建議您在任何不專供套件外使用的新 .bzl
檔案頂端新增 visibility("private")
。
範例
# //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
...
載入瀏覽權限做法
本節說明管理載入瀏覽權限宣告的訣竅。
因式分解顯示設定
如果多個 .bzl
檔案應具備相同的瀏覽權限,建議您將套件規格整理成常見清單。例如:
# //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)
...
這有助於防止不同 .bzl
檔案瀏覽權限之間的意外偏移。此外,如果 clients
清單較大,也更容易判讀。
撰寫瀏覽權限
有時,.bzl
檔案可能需要顯示在由多個小型許可清單組成的許可清單中。這類似於 package_group
如何透過其 includes
屬性整合其他 package_group
。
假設您要淘汰廣為使用的巨集。您需要僅供現有使用者,以及自家團隊擁有的套件查看。您可能會寫:
# //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)
複製套件群組
與目標瀏覽權限不同,您無法在 package_group
中定義載入瀏覽權限。如要重複使用相同的許可清單來進行目標瀏覽權限和載入瀏覽權限,建議您將套件規格清單移至 .bzl 檔案中,讓這兩種宣告可能參照。根據上方考量瀏覽權限中的範例,您可以撰寫:
# //mylib/BUILD
load(":internal_defs", "clients")
package_group(
name = "my_pkg_grp",
packages = clients,
)
只有在清單不含任何排除套件規格時,才有作用。
保護個別符號
無法從其他檔案載入名稱以底線開頭的任何 Starlark 符號。這可讓您輕鬆建立私人符號,但無法讓您與一組信任的檔案共用這些符號。另一方面,載入瀏覽權限可讓您控制其他套件可以查看 .bzl file
的內容,但不允許您防止系統載入任何非底線的符號。
幸好,您可以結合使用這兩項功能,取得精細的控制選項。
# //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
bzl-visibility 建構工具 Lint
如果使用者的檔案本身不在該目錄父項之下,當使用者從名為 internal
或 private
的目錄載入檔案時,系統會提供 Buildifier Lint 的警告訊息。這項 Lint 會預先載入載入瀏覽權限功能,因此對於 .bzl
檔案宣告瀏覽權限的工作區不需要使用。