このページでは、Bazel の 3 つの公開設定システム( ターゲットの公開設定、 推移的な公開設定、 読み込みの公開設定)について説明します。
これらのタイプの公開設定は、ライブラリの公開 API と実装の詳細を区別するのに役立ち、ワークスペースの拡大に伴って構造を強化するのに役立ちます。また、公開 API を非推奨にするときに、公開設定を使用して、現在のユーザーは許可し、新しいユーザーを拒否することもできます。
ターゲットの公開設定
ターゲットの公開設定 は、ターゲットに依存できるユーザー(deps などの属性内でターゲットのラベルを使用できるユーザー)を制御します。ターゲットが依存関係のいずれかの公開設定に違反している場合、分析フェーズでビルドに失敗します。
通常、ターゲット A は、ターゲット B と同じ場所にある場合、または A が B の場所に公開設定を付与する場合に、ターゲット 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 パラメータで設定できます。このパラメータが指定されていない場合、公開設定は公開になります。
`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 は
config_setting ターゲットの公開設定を適用していませんでした。select()この以前の動作を削除するフラグは 2 つあります。
--incompatible_enforce_config_setting_visibilityは、これらのターゲットの公開設定チェックを有効にします。移行を支援するため、config_settingを指定しないvisibilityは、パッケージ レベルのdefault_visibilityに関係なく、 公開と見なされます。--incompatible_config_setting_private_default_visibilityを指定すると、config_settingを指定しない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 ターゲットに関してチェックされます。
ルールの使用を特定のパッケージに制限する場合は、代わりに 読み込みの公開設定を使用します。
公開設定とシンボリック マクロ
このセクションでは、公開設定システムと シンボリック マクロの相互作用について説明します。
シンボリック マクロ内の場所
公開設定システムの重要な点は、宣言の場所を特定する方法です。シンボリック マクロで宣言されていないターゲットの場合、場所はターゲットが存在するパッケージ(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 ファイルを読み込むことはできますが、srcs の filegroup にリストすることはできません。
その逆も同様です。これにより、ドキュメントの生成やテストなど、.bzl ファイルをソースコードとして使用するルールで問題が発生することがあります。
プロトタイピングでは、--check_bzl_visibility=false を設定して、読み込みの公開設定の適用を無効にできます。--check_visibility=false と同様に、送信されたコードには使用しないでください。
読み込みの公開設定は、Bazel 6.0 以降で使用できます。
読み込みの公開設定を宣言する
.bzl ファイルの読み込みの公開設定を設定するには、ファイル内から
visibility() 関数を呼び出します。
visibility() の引数は、
packages の
package_group 属性と同様に、パッケージ指定のリストです。ただし、visibility() は負のパッケージ指定を受け付けません。
visibility() の呼び出しは、ファイルごとに 1 回だけ、最上位レベル(関数内ではない)で行う必要があります。理想的には、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 が他の package_group を介してその
includes 属性を組み込む方法に似ています。
広く使用されているマクロを非推奨にするとします。既存のユーザーと自分のチームが所有するパッケージにのみ表示されるようにします。次のように記述します。
# //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 リンター
ユーザーのファイルがそのディレクトリの親の下にない場合、ユーザーが internal または private という名前のディレクトリからファイルを読み込むと、警告が表示される Buildifier リンターがあります。このリンターは読み込みの公開設定機能よりも前に存在し、.bzl ファイルが公開設定を宣言するワークスペースでは不要です。