このページでは、Bazel の 3 つの公開設定システム(ターゲットの公開設定、推移的な公開設定、読み込みの公開設定)について説明します。
これらのタイプの可視性により、他のデベロッパーはライブラリの公開 API と実装の詳細を区別でき、ワークスペースの拡大に合わせて構造を適用できます。また、公開 API を非推奨にする際に、可視性を使用して、現在のユーザーを許可し、新しいユーザーを拒否することもできます。
ターゲットの可視性
ターゲットの公開設定は、ターゲットに依存するユーザー(deps などの属性内でターゲットのラベルを使用できるユーザー)を制御します。依存関係のいずれかの公開設定に違反している場合、分析フェーズ中にターゲットのビルドが失敗します。
一般に、ターゲット 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 で定義されたターゲットでは使用できません。
ベスト プラクティス: 同じパッケージ セットに複数のターゲットを表示するには、各ターゲットの visibility 属性でリストを繰り返すのではなく、package_group を使用します。これにより、読みやすさが向上し、リストの同期が解除されるのを防ぐことができます。
ベスト プラクティス: 別のチームのプロジェクトに公開設定を付与する場合は、__pkg__ ではなく __subpackages__ を使用して、プロジェクトの進化や新しいサブパッケージの追加に伴う不要な公開設定の変更を回避します。
ルールのターゲットの公開設定
ルール ターゲットの公開設定は、visibility 属性(指定されていない場合は適切なデフォルト)を取得し、ターゲットが宣言された場所を追加することで決定されます。シンボリック マクロで宣言されていないターゲットの場合、パッケージで default_visibility が指定されている場合は、このデフォルトが使用されます。他のすべてのパッケージと、シンボリック マクロで宣言されたターゲットの場合、デフォルトは ["//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/..."],
)
ベスト プラクティス: default_visibility を公開に設定しないでください。プロトタイピングや小規模なコードベースでは便利かもしれませんが、コードベースが大きくなるにつれて、誤って公開ターゲットを作成するリスクが高まります。パッケージのパブリック インターフェースの一部であるターゲットを明示することをおすすめします。
生成されたファイルのターゲットの公開設定
生成されたファイル ターゲットの公開設定は、それを生成するルール ターゲットと同じです。
# //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",
]
...
)
ソースファイルのターゲットの公開設定
ソースファイル ターゲットは、exports_files を使用して明示的に宣言するか、ルールのラベル属性(シンボリック マクロの外部)でファイル名を参照して暗黙的に作成できます。ルールのターゲットと同様に、exports_files の呼び出しの場所、または入力ファイルを参照した BUILD ファイルは、常にファイルの公開設定に自動的に追加されます。
exports_files で宣言されたファイルは、その関数の visibility パラメータによって公開設定を設定できます。このパラメータを指定しない場合、公開設定は public になります。
exports_files の呼び出しに表示されないファイルの場合、公開設定はフラグ --incompatible_no_implicit_file_export の値によって異なります。
フラグが true の場合、公開設定は非公開です。
それ以外の場合は、以前の動作が適用されます。公開設定は
BUILDファイルのdefault_visibilityと同じになります。デフォルトの公開設定が指定されていない場合は非公開になります。
以前の動作に依存しないようにしてください。ソースファイル ターゲットに非公開以外の公開設定が必要な場合は、必ず exports_files 宣言を記述します。
ベスト プラクティス: 可能であれば、ソースファイルではなくルール ターゲットを公開することをおすすめします。たとえば、.java ファイルで exports_files を呼び出す代わりに、ファイルを非公開の java_library ターゲットでラップします。一般に、ルール ターゲットは、同じパッケージに存在するソースファイルのみを直接参照する必要があります。
例
ファイル //frobber/data/BUILD:
exports_files(["readme.txt"])
ファイル //frobber/bin/BUILD:
cc_binary(
name = "my-program",
data = ["//frobber/data:readme.txt"],
)
構成設定の公開設定
これまで、Bazel は select() のキーで参照される config_setting ターゲットの公開設定を適用していませんでした。この以前の動作を削除するには、次の 2 つのフラグを使用します。
--incompatible_enforce_config_setting_visibilityを使用すると、これらのターゲットの可視性チェックを有効にできます。移行を支援するため、visibilityを指定しないconfig_settingは(パッケージ レベルのdefault_visibilityに関係なく)公開と見なされます。--incompatible_config_setting_private_default_visibilityを指定すると、visibilityを指定しないconfig_settingは、他のルール ターゲットと同様に、パッケージのdefault_visibilityを尊重し、非公開の公開設定にフォールバックします。--incompatible_enforce_config_setting_visibilityが設定されていない場合は、no-op です。
以前の動作に依存しないようにしてください。現在のパッケージの外部で使用することを目的とした config_setting には、パッケージで適切な default_visibility が指定されていない場合、明示的な visibility を指定する必要があります。
パッケージ グループのターゲットの公開設定
package_group ターゲットには visibility 属性がありません。常に一般公開されます。
暗黙的な依存関係の可視性
一部のルールには、暗黙的な依存関係があります。これは、BUILD ファイルに明示されていないものの、そのルールのすべてのインスタンスに固有の依存関係です。たとえば、cc_library ルールは、各ルール ターゲットから C++ コンパイラを表す実行可能ターゲットへの暗黙的な依存関係を作成する場合があります。
このような暗黙的な依存関係の公開設定は、ルール(またはアスペクト)が定義されている .bzl ファイルを含むパッケージに関してチェックされます。この例では、C++ コンパイラは cc_library ルールの定義と同じパッケージに存在する限り、非公開にできます。フォールバックとして、暗黙的な依存関係が定義から参照できない場合は、cc_library ターゲットに関してチェックされます。
ルールの使用を特定のパッケージに制限する場合は、代わりに読み込みの公開設定を使用します。
可視性とシンボリック マクロ
このセクションでは、可視性システムがシンボリック マクロとどのようにやり取りするかについて説明します。
シンボリック マクロ内のロケーション
公開設定システムの重要な詳細として、宣言の場所を特定する方法があります。シンボリック マクロで宣言されていないターゲットの場合、場所はターゲットが存在するパッケージ(BUILD ファイルのパッケージ)です。ただし、シンボリック マクロで作成されたターゲットの場合、場所はマクロの定義(my_macro = macro(...) ステートメント)が表示される .bzl ファイルを含むパッケージです。複数のネストされたターゲット内にターゲットが作成される場合、常に最も内側のシンボリック マクロの定義が使用されます。
同じシステムを使用して、特定の依存関係の可視性を確認する場所を決定します。使用するターゲットがマクロ内で作成された場合、使用するターゲットが存在するパッケージではなく、最も内側のマクロの定義が参照されます。
つまり、コードが同じパッケージで定義されているすべてのマクロは、自動的に相互に「フレンド」になります。//lib:defs.bzl で定義されたマクロによって直接作成されたターゲットは、マクロが実際にインスタンス化されるパッケージに関係なく、//lib で定義された他のマクロから参照できます。同様に、//lib/BUILD とそのレガシー マクロで直接宣言されたターゲットを参照でき、それらのターゲットから参照されることもあります。逆に、同じパッケージに存在するターゲットは、少なくとも 1 つがシンボリック マクロによって作成されている場合、必ずしも相互に参照できるとは限りません。
シンボリック マクロの実装関数内では、visibility パラメータには、マクロが呼び出された場所を追加した後のマクロの visibility 属性の有効な値が含まれます。マクロがそのターゲットの 1 つを呼び出し元にエクスポートする標準的な方法は、some_rule(..., visibility = visibility) のように、この値をターゲットの宣言に転送することです。この属性を省略したターゲットは、呼び出し元がマクロ定義と同じパッケージにない限り、マクロの呼び出し元には表示されません。この動作は、サブマクロへのネストされた呼び出しのチェーンがそれぞれ visibility = visibility を渡し、内部マクロのエクスポートされたターゲットを各レベルで呼び出し元に再エクスポートし、マクロの実装の詳細を公開しないという意味で構成されます。
サブマクロに権限を委任する
可視性モデルには、マクロがその権限をサブマクロに委任できるようにする特別な機能があります。これは、マクロの因数分解と作成に重要です。
別のパッケージのルール some_library を使用して依存関係エッジを作成するマクロ my_macro があるとします。
# //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", ...)
//pkg:foo_dependency ターゲットには visibility が指定されていないため、//macro 内でのみ表示されます。これは、コンシューマ ターゲットで正常に機能します。ここで、//lib の作成者が some_library をリファクタリングして、マクロを使用して実装するとどうなるでしょうか。
# //lib:defs.bzl
def _impl(name, visibility, deps, ...):
some_rule(
# Main target, exported.
name = name,
visibility = visibility,
deps = deps,
...)
some_library = macro(implementation = _impl, ...)
この変更により、//pkg:foo_consumer の場所は //macro ではなく //lib になったため、//pkg:foo_dependency の使用は依存関係の公開設定に違反します。my_macro の作成者が、この実装の詳細を回避するためだけに、依存関係の宣言に visibility = ["//lib"] を渡すことは想定できません。
このため、ターゲットの依存関係が、ターゲットを宣言したマクロの属性値でもある場合、依存関係の公開設定は、使用するターゲットの場所ではなく、マクロの場所に対してチェックされます。
この例では、//pkg:foo_consumer が //pkg:foo_dependency を参照できるかどうかを検証するために、//pkg:foo_dependency が my_macro 内の some_library の呼び出しに入力として渡されていることを確認し、代わりにこの呼び出しの場所 //macro に対して依存関係の可視性をチェックします。
このプロセスは、ターゲットまたはマクロ宣言が、ラベル型の属性の 1 つに依存関係のラベルを受け取る別のシンボリック マクロ内にある限り、再帰的に繰り返すことができます。
ファイナライザ
ルール ファイナライザ(finalizer = True を含むシンボリック マクロ)で宣言されたターゲットは、通常のシンボリック マクロの公開ルールに従うターゲットに加えて、ファイナライザ ターゲットのパッケージに公開されているすべてのターゲットも参照できます。
つまり、native.existing_rules() ベースのレガシー マクロをファイナライザに移行しても、ファイナライザによって宣言されたターゲットは、古い依存関係を引き続き参照できます。
ファイナライザが native.existing_rules() を使用してイントロスペクトできるものの、可視性システムで依存関係として使用できないターゲットを定義できます。たとえば、マクロ定義のターゲットが、独自のパッケージまたはファイナライザ マクロの定義に表示されず、ファイナライザに委任されていない場合、ファイナライザはそのターゲットを認識できません。ただし、native.existing_rules() ベースのレガシー マクロでも、このようなターゲットは表示されません。
推移的な可視性
推移的な公開設定は、依存関係が間接的な場合も含めて、ターゲットに依存するユーザーを制限する方法です。これは通常のターゲット可視性システムとは別に適用されるため、依存関係チェーン内のすべてのリンクが有効であっても、推移的な可視性エラーが発生する可能性があります。これにより、ターゲットの作成者は、信頼できる一連のクライアント ターゲットと共有しながら、ビルドの残りの部分に誤って漏洩しないように保護できます。
推移的な公開設定は、[package][package] 関数の transitive_visibility パラメータを使用して、パッケージ レベルで宣言されます。この宣言は、パッケージ内のすべてのターゲットに適用されます。このパラメータは package_group の単一のラベルを受け取ります。このラベルは、ターゲットがこのパッケージのターゲットに推移的に依存する可能性があるパッケージを指定します。
# //third_party/sensitive_dep/BUILD
package(
# This must be explicitly listed
transitive_visibility = ":sensitive_dep_users",
)
package_group(
name = "sensitive_dep_users",
packages = [
"//third_party/sensitive_dep/...", # has to include itself
"//allowed",
],
)
sh_library(
name = "sensitive_dep"
)
# //not_allowed/BUILD
sh_binary(
name = "bad",
deps = ["//third_party/sensitive_dep:sensitive_dep"]
)
//third_party/sensitive_dep:sensitive_dep に依存するが、transitive_visibility の一部ではない //not_allowed:bad をビルドすると、次のエラーが発生します。
Transitive visibility error: //third_party/sensitive_dep:sensitive_dep is not transitively visible from ///not_allowed:bad. //"//third_party/sensitive_dep:sensitive_dep inherits a transitive_visibility declaration from its package or one of its dependencies that does not allow //not_allowed:bad.
transitive_visibility で使用されるパッケージ グループは、他のパッケージ グループを include できません。
ターゲットに推移的な公開制限を持つ推移的な依存関係がある場合、そのターゲット自体が制限を継承し、そのターゲットに依存するターゲットに渡します。ターゲットに推移的な公開制限を持つ複数の推移的な依存関係がある場合、すべての制限を満たす必要があります。
推移的な可視性は、シンボリック マクロで宣言されたターゲットにも適用されます。ただし、通常のターゲットの公開設定とは異なり、シンボリック マクロを定義したパッケージではなく、これらのターゲットが存在するパッケージのみが考慮されます。
負荷の可視化
読み込みの公開設定は、現在のパッケージ外の他の 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() の呼び出しは、ファイルごとに 1 回だけ、トップレベル(関数内ではない)で行う必要があります。理想的には、load() ステートメントの直後に行います。
ターゲットの公開設定とは異なり、デフォルトの読み込みの公開設定は常に public です。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 を参照できるかどうかを制御できますが、アンダースコアのないシンボルが読み込まれないようにすることはできません。
幸いなことに、この 2 つの機能を組み合わせることで、きめ細かい制御が可能になります。
# //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
Buildifier lint があり、ユーザーが internal または private という名前のディレクトリからファイルを読み込むときに、ユーザーのファイルがそのディレクトリの親の下にない場合に警告が表示されます。この lint は、読み込みの可視性機能よりも前に作成されたもので、.bzl ファイルで可視性が宣言されているワークスペースでは不要です。