可視性

問題を報告する ソースを表示 Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

このページでは、Bazel の 2 つの可視性システム(ターゲットの可視性読み込みの可視性)について説明します。

どちらの可視性も、他のデベロッパーがライブラリの公開 API と実装の詳細を区別するのに役立ち、ワークスペースの拡大に伴って構造を強制するのに役立ちます。公開 API を非推奨にするときに、可視性を使用して、既存のユーザーは許可し、新規ユーザーは拒否することもできます。

ターゲットの可視性

ターゲットの可視性は、ターゲットに依存できるユーザー(deps などの属性内でターゲットのラベルを使用できるユーザー)を制御します。ターゲットが依存関係のいずれかの可視性を侵害している場合、分析フェーズでビルドに失敗します。

一般的に、ターゲット A は、ターゲット B と同じ場所にある場合、または AB の場所への可視性を付与している場合に、ターゲット 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:mytargetvisibility[":__subpackages__", "//tests:__pkg__"] に設定されている場合、//some/package/... ソースツリーの一部であるターゲットと //tests/BUILD で宣言されたターゲットはこれを使用できますが、//tests/integration/BUILD で定義されたターゲットは使用できません。

ベスト プラクティス: 複数のターゲットを同じパッケージのセットに表示するには、各ターゲットの visibility 属性でリストを繰り返すのではなく、package_group を使用します。これにより、可読性が向上し、リストの同期が解除されるのを防ぐことができます。

ベスト プラクティス: 別のチームのプロジェクトに可視性を付与する場合は、__subpackages____pkg__ よりも優先して使用します。これにより、プロジェクトの進化や新しいサブパッケージの追加に伴う不要な可視性の変更を回避できます。

ルールのターゲットの公開設定

ルール ターゲットの可視性は、その 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 は 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_dependencymy_macro 内の some_library の呼び出しへの入力としても渡されていることを確認し、この呼び出しのロケーション //macro に対して依存関係の可視性を確認します。

このプロセスは、ターゲットまたはマクロの宣言が、依存関係のラベルをラベル型の属性の 1 つに取る別のシンボリック マクロ内にある限り、再帰的に繰り返すことができます。

ファイナライザー

ルール ファイナライザー(finalizer = True を含むシンボリック マクロ)で宣言されたターゲットは、通常のシンボリック マクロの可視性ルールに従うターゲットに加えて、ファイナライザー ターゲットのパッケージに可視のすべてのターゲットも確認できます。

つまり、native.existing_rules() ベースのレガシー マクロをファイナライザーに移行しても、ファイナライザーで宣言されたターゲットは古い依存関係を引き続き参照できます。

native.existing_rules() を使用してファイナライザーがイントロスペクションできるが、可視性システムで依存関係として使用できないターゲットを定義できます。たとえば、マクロ定義のターゲットが独自のパッケージまたはファイナライザー マクロの定義に表示されず、ファイナライザーに委任されていない場合、ファイナライザーはそのようなターゲットを確認できません。ただし、native.existing_rules() ベースの以前のマクロも、このようなターゲットを参照できません。

読み込みの可視性

読み込みの可視性は、現在のパッケージ外の他の BUILD ファイルまたは .bzl ファイルから .bzl ファイルを読み込めるかどうかを制御します。

ターゲットの可視性によってターゲットでカプセル化されたソースコードが保護されるのと同様に、読み込みの可視性によって .bzl ファイルでカプセル化されたビルドロジックが保護されます。たとえば、BUILD ファイルの作成者は、繰り返されるターゲット宣言を .bzl ファイルのマクロにファクタリングしたい場合があります。ロードの可視性が保護されていないと、同じワークスペース内の他の共同編集者がマクロを再利用する可能性があり、マクロを変更すると他のチームのビルドが壊れる可能性があります。

.bzl ファイルに、対応するソースファイル ターゲットがある場合とない場合があります。一致する場合でも、負荷の可視性とターゲットの可視性が一致する保証はありません。つまり、同じ BUILD ファイルが .bzl ファイルを読み込むことはできても、filegroupsrcs に一覧表示することはできない、またはその逆の可能性があります。これにより、ドキュメントの生成やテストなど、.bzl ファイルをソースコードとして使用するルールで問題が発生することがあります。

プロトタイピングでは、--check_bzl_visibility=false を設定して読み込みの可視性の適用を無効にできます。--check_visibility=false と同様に、送信されたコードに対しては行わないでください。

読み込みの可視性は Bazel 6.0 以降で使用できます。

読み込みの公開設定を宣言する

.bzl ファイルの読み込みの可視性を設定するには、ファイル内から visibility() 関数を呼び出します。visibility() の引数は、package_grouppackages 属性と同様に、パッケージ仕様のリストです。ただし、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_groupincludes 属性を介して他の 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 ファイルで可視性を宣言するワークスペースでは不要です。