可視性

問題を報告 ソースを表示

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

どちらの公開設定も、他のデベロッパーがライブラリの公開 API とその実装の詳細を区別するのに役立ちます。また、ワークスペースの拡大に合わせて構造を適用することもできます。公開 API を非推奨にするときに可視性を使用して、現在のユーザーを許可し、新しいユーザーを拒否することもできます。

ターゲットの公開設定

ターゲットの公開設定は、ターゲットに依存するユーザー、つまり deps などの属性内でターゲットのラベルを使用できるユーザーを制御します。

ターゲット A は、同じパッケージ内にある場合、または AB のパッケージの公開設定を許可している場合、ターゲット B から参照できます。このように、パッケージはアクセスを許可するかどうかを決定するための粒度単位です。BA に依存していて、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 を使用します。これにより、読みやすくなり、リストが同期しなくなることを防止できます。

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

ルール ターゲットの公開設定は次のようになります。

  1. visibility 属性の値(設定されている場合)。それ以外の場合は、

  2. ターゲットの BUILD ファイル内の package ステートメントの default_visibility 引数の値(そのような宣言が存在する場合)。それ以外の場合は、

  3. //visibility:private)。

ベスト プラクティス: default_visibility を public に設定しないでください。プロトタイピングや小規模なコードベースでは便利ですが、コードベースが大きくなるにつれて、誤って公開ターゲットを作成するリスクが高くなります。パッケージの公開インターフェースに含まれるターゲットを明示的に指定することをおすすめします。

ファイル //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 を呼び出すことで、ソースファイルのターゲットの公開設定を明示的に設定できます。exports_filesvisibility 引数を渡さない場合、公開設定を公開します。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 が設定されていない場合、処理は行われません。

従来の動作に依存しないでください。現在のパッケージの外部で使用される 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 ファイルを読み込むことはできても、filegroupsrcs にはリストできないことがあります。また、その逆も可能です。このため、ドキュメントの生成やテストなどで、.bzl ファイルをソースコードとして使用するルールで問題が発生することがあります。

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

負荷の可視性は Bazel 6.0 以降で使用できます。

読み込みの公開設定の宣言

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

Buildifier lint があり、ユーザーが internal または private という名前のディレクトリからファイルを読み込んだときに、ユーザーのファイルがそのディレクトリの親の下にない場合に、警告が表示されます。この lint は、読み込みの可視性機能より前から存在するもので、.bzl ファイルで可視性を宣言するワークスペースでは不要です。