本頁面將介紹 Bazel 的兩種瀏覽權限系統:目標瀏覽權限和載入瀏覽權限。
這兩種可見度都有助於其他開發人員區分程式庫的公開 API 和實作詳細資料,並在工作區擴大時強制執行結構。您也可以在淘汰公開 API 時使用可見度,允許現有使用者存取,但拒絕新使用者存取。
目標曝光度
目標可見度可控管哪些人可依附目標,也就是哪些人可在 deps
等屬性中使用目標的標籤。
如果目標 A
和目標 B
位於同一個套件中,或是 A
授予 B
套件的顯示權,目標 B
就會看到目標 A
。因此,套件是決定是否允許存取的細微程度單位。如果 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
s (未指定visibility
) 的default_visibility
遵守套件的default_visibility
,並回退至私人顯示設定,就像任何其他規則目標一樣。如果未設定--incompatible_enforce_config_setting_visibility
,則為無運算。
請避免依賴舊版行為。如果要在目前套件以外使用任何 config_setting
,且套件尚未指定合適的 default_visibility
,則應明確指定 visibility
。
套件群組目標瀏覽權限
package_group
目標缺少 visibility
屬性。這些資訊一律會公開顯示。
隱含依附元件的瀏覽權限
部分規則具有隱含依附元件,也就是未在 BUILD
檔案中明確列出,但每個規則例項都內建的依附元件。舉例來說,cc_library
規則可能會從每個規則目標建立隱含依附元件,指向代表 C++ 編譯器的可執行目標。
系統會根據包含規則 (或構面) 所定義 .bzl
檔案的套件,檢查這類隱含依附元件的瀏覽權限。在我們的範例中,只要 C++ 編譯器與 cc_library
規則的定義位於同一個套件中,就可以是私有編譯器。如果定義中未顯示隱含依附元件,系統會檢查 cc_library
目標。
如要變更這項行為,請停用 --incompatible_visibility_private_attributes_at_definition
。停用後,系統會將隱含依附元件視為其他依附元件。也就是說,規則的每個例項都必須能看到所依附的目標 (例如 C++ 編譯器)。在實務上,這通常表示目標必須公開顯示。
如要將規則的使用限制在特定套件,請改用載入曝光度。
載入瀏覽權限
載入顯示狀態:控制是否可從目前套件以外的其他 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 Buildifier lint
如果使用者從名為 internal
或 private
的目錄載入檔案,但使用者檔案本身並非位於該目錄的父項下方,Buildifier Lint 就會提供警告。這項 Lint 檢查早於載入可見度功能,在 .bzl
檔案宣告可見度的情況下,工作區不需要這項檢查。