このページでは、Bazel の 2 つの可視性システム(ターゲットの可視性と読み込みの可視性)について説明します。
どちらの可視性も、他のデベロッパーがライブラリの公開 API と実装の詳細を区別するのに役立ち、ワークスペースの拡大に伴って構造を強制するのに役立ちます。公開 API を非推奨にするときに、可視性を使用して、既存のユーザーは許可し、新規ユーザーは拒否することもできます。
ターゲットの可視性
ターゲットの可視性は、ターゲットに依存できるユーザー(deps
などの属性内でターゲットのラベルを使用できるユーザー)を制御します。
ターゲット A
は、ターゲット B
と同じパッケージ内にある場合、または A
が B
のパッケージに可視性を付与している場合に、B
に可視になります。したがって、パッケージはアクセスを許可するかどうかを決定する粒度の単位です。B
が A
に依存しているにもかかわらず、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
を使用します。これにより、可読性が向上し、リストの同期が解除されるのを防ぐことができます。
ルールのターゲットの公開設定
ルールのターゲットの可視性は次のとおりです。
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
ファイルで 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++ コンパイラを表す実行可能ターゲットへの暗黙的な依存関係を作成する場合があります。
現在、可視性の目的で、これらの暗黙的な依存関係は他の依存関係と同様に扱われます。つまり、依存しているターゲット(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()
の呼び出しは、ファイルごとに 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
ユーザーのファイルがそのディレクトリの親の下にない場合、ユーザーが internal
または private
という名前のディレクトリからファイルを読み込むと、警告を表示する Buildifier lint があります。この lint は読み込みの可視性機能よりも前に存在しており、.bzl
ファイルで可視性を宣言するワークスペースでは不要です。