可視性

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

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

ターゲットの可視性

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

ターゲット A は、ターゲット B と同じパッケージ内にある場合、または 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 を一般公開に設定しないでください。プロトタイピングや小規模なコードベースでは便利ですが、コードベースが大きくなるにつれて、誤って公開ターゲットを作成するリスクが高まります。パッケージの公開インターフェースの一部であるターゲットを明示的に指定することをおすすめします。

ファイル //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 では、 config_setting ターゲットの可視性は適用されていませんでした。select()この以前の動作を削除するには、次の 2 つのフラグがあります。

  • --incompatible_enforce_config_setting_visibility は、これらのターゲットの可視性チェックを有効にします。移行を支援するため、 config_setting を指定しない visibility は、パッケージ レベルの default_visibility に関係なく、 一般公開と見なされます。

  • --incompatible_config_setting_private_default_visibility を指定しない config_setting は、他のルール ターゲットと同様に、パッケージの default_visibility を尊重し、非公開の可視性にフォールバックします。visibility--incompatible_enforce_config_setting_visibility が設定されていない場合は、何も行いません。

以前の動作に依存することは避けてください。現在のパッケージ外で使用する config_setting は、パッケージで適切な default_visibility が指定されていない場合は、明示的な visibility を設定する必要があります。

パッケージ グループ ターゲットの可視性

package_group ターゲットには visibility 属性はありません。常に一般公開されます。

暗黙的な依存関係の可視性

一部のルールには暗黙的な依存関係があります。これは、 BUILD ファイルに記述されていませんが、 そのルールのすべてのインスタンスに固有の依存関係です。たとえば、cc_library ルールは、各ルール ターゲットから C++ コンパイラを表す実行可能ターゲットへの暗黙的な依存関係を作成する場合があります。

現在、可視性の目的で、これらの暗黙的な依存関係は他の依存関係と同様に扱われます。つまり、依存しているターゲット(C++ コンパイラなど)は、ルールのすべてのインスタンスから参照できる必要があります。実際には、通常、ターゲットの可視性は一般公開にする必要があります。

この動作を変更するには、 --incompatible_visibility_private_attributes_at_definition を設定します。有効にすると、問題のターゲットは、暗黙的な依存関係を宣言するルールからのみ参照可能になります。つまり、ルールが定義されている .bzl ファイルを含むパッケージから参照できる必要があります。この例では、C++ コンパイラは、cc_library ルールの定義と同じパッケージ内にある限り、非公開にできます。

読み込みの可視性

読み込みの可視性 は、.bzl ファイルを他の BUILD ファイルまたは .bzl ファイルから読み込めるかどうかを制御します。

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

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

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

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

読み込みの可視性を宣言する

.bzl ファイルの読み込みの可視性を設定するには、ファイル内で visibility() 関数を呼び出します。 visibility() の引数は、 packagespackage_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 lintが警告を表示します。このリンターは読み込みの可視性機能よりも前に存在し、.bzl ファイルが可視性を宣言するワークスペースでは不要です。