マクロを使用してカスタム動詞を作成する

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。
問題を報告する ソースを表示

Bazel との日々のやり取りは、主に buildtestrun などのコマンドで行います。しかし、リポジトリにパッケージを push したり、エンドユーザー向けのドキュメントを公開したり、Kubernetes を使用してアプリケーションをデプロイしたりする必要はあると考えるかもしれません。Bazel には publishdeploy コマンドがありませんが、これらのアクションをどこに位置付けるかを指定します。

bazel run コマンド

Bazel は密閉性、再現性、インクリメンタリティを重視しているため、build コマンドと test コマンドは上記のタスクには役立ちません。これらのアクションは、ネットワーク アクセスが制限されたサンドボックス内で実行される可能性があり、すべての bazel build で再度実行される保証はありません。

代わりに、bazel run を使用します。これは、副作用を発生させるタスクの主力です。Bazel のユーザーは実行可能ファイルを作成するルールに慣れています。ルールの作成者は共通のパターンセットに従ってこれを「カスタム動詞」に拡張できます。

野生: rule_k8s

たとえば、Bazel の Kubernetes ルール rules_k8s について考えてみましょう。次のようなターゲットがあるとします。

# BUILD file in //application/k8s
k8s_object(
    name = "staging",
    kind = "deployment",
    cluster = "testing",
    template = "deployment.yaml",
)

staging ターゲットで bazel build を使用すると、k8s_object ルールによって標準の Kubernetes YAML ファイルがビルドされます。ただし、追加のターゲットは、staging.apply:staging.delete などの名前で k8s_object マクロによっても作成されます。これらのビルド スクリプトは、これらのアクションを実行します。bazel run staging.apply で実行すると、独自の bazel k8s-apply コマンドまたは bazel k8s-delete コマンドのように動作します。

別の例: ts_api_guardian_test

このパターンは、Angular プロジェクトでも見ることができます。ts_api_guardian_test マクロは 2 つのターゲットを生成します。1 つ目は標準の nodejs_test ターゲットです。生成された出力を「ゴールデン」ファイル(予想される出力を含むファイル)と比較します。これは、通常の bazel test 呼び出しでビルドして実行できます。angular-cli では、bazel test //etc/api:angular_devkit_core_api を使用して、このようなターゲットを 1 つ実行できます。

今後、正当な理由で、このゴールデン ファイルの更新が必要になることがあります。手動で更新するのは手間がかかり、エラーが発生しやすいため、このマクロでは nodejs_binary ファイルとゴールデン ファイルも比較して比較が行われます。実質的には、呼び出し方法に基づいて、同じテスト スクリプトを「検証」モードまたは「承認」モードで実行するように記述できます。これは、すでに学習したのと同じパターンに従います。ネイティブの bazel test-accept コマンドはありませんが、bazel run //etc/api:angular_devkit_core_api.accept を使用しても同じ効果が得られます。

このパターンは非常に強力であり、認識するとすぐに一般的になります。

独自のルールの適応

マクロは、このパターンの中心です。マクロはルールと同様に使用されますが、複数のターゲットを作成できます。通常は、指定された名前のターゲットを作成し、プライマリ ビルド アクションを実行します。これにより、通常のバイナリ、Docker イメージ、ソースコードのアーカイブがビルドされる可能性があります。このパターンでは、結果のターゲットのバイナリ公開や、想定されるテスト出力の更新など、プライマリ ターゲットの出力に基づいて副作用を実行するスクリプトを作成するためのターゲットが新たに作成されます。

前述の例では、マクロを生成する Sphinx を使用してウェブサイトを生成する架空のルールをラップし、ユーザーが準備ができたときに公開できる追加ターゲットを作成します。Sphinx を使用してウェブサイトを生成する場合は、次の既存のルールを検討してください。

_sphinx_site = rule(
     implementation = _sphinx_impl,
     attrs = {"srcs": attr.label_list(allow_files = [".rst"])},
)

次に、次のようなルールを考えてみましょう。このスクリプトは、実行時に生成されたページを公開するスクリプトを作成します。

_sphinx_publisher = rule(
    implementation = _publish_impl,
    attrs = {
        "site": attr.label(),
        "_publisher": attr.label(
            default = "//internal/sphinx:publisher",
            executable = True,
        ),
    },
    executable = True,
)

最後に、次のマクロを定義して、上記の両方のルールのターゲットをまとめて作成します。

def sphinx_site(name, srcs = [], **kwargs):
    # This creates the primary target, producing the Sphinx-generated HTML.
    _sphinx_site(name = name, srcs = srcs, **kwargs)
    # This creates the secondary target, which produces a script for publishing
    # the site generated above.
    _sphinx_publisher(name = "%s.publish" % name, site = name, **kwargs)

BUILD ファイルで、プライマリ ターゲットを作成するだけの場合と同様に、マクロを使用します。

sphinx_site(
    name = "docs",
    srcs = ["index.md", "providers.md"],
)

この例では、ドキュメントが 1 つの Bazel ルールであるように、ドキュメントがターゲットとして作成されます。ルールを構築すると、いくつかの構成が生成され、Sphinx が実行されて手動で検査できる HTML サイトが作成されます。ただし、追加の「docs.publish」ターゲットも作成され、サイトを公開するためのスクリプトが作成されます。プライマリ ターゲットの出力を確認したら、架空の bazel publish コマンドと同様に、bazel run :docs.publish を使用して公開できます。

_sphinx_publisher ルールの実装がどのようになるかは、すぐにはわかりません。このようなアクションは多くの場合、ランチャー シェル スクリプトを記述します。この方法では、ctx.actions.expand_template を使用して非常に単純なシェル スクリプトを記述する必要があります。この場合、プライマリ ターゲットの出力へのパスを指定してパブリッシャー バイナリを呼び出します。このように、パブリッシャーの実装は汎用のままで、_sphinx_site ルールによって HTML のみが生成されます。この小さなスクリプトは、この 2 つを組み合わせるために必要なものです。

rules_k8s では、実際に .apply が実行します。expand_templateapply.sh.tpl に基づいて非常に単純な Bash スクリプトを記述します。このスクリプトは、プライマリ ターゲットの出力で kubectl を実行します。このスクリプトは、bazel run :staging.apply を使用してビルドおよび実行することができ、実質的には、k8s_object ターゲットのための k8s-apply コマンドを提供します。