.bzl スタイルガイド

このページでは、Starlark の基本的なスタイルガイドについて説明します。また、マクロとルールに関する情報も記載しています。

Starlark は、ソフトウェアのビルド方法を定義する 言語であり、プログラミング言語と構成言語の両方の 側面を持っています。

Starlark を使用して、BUILD ファイル、マクロ、ビルドルールを記述します。マクロとルールは、本質的にメタ言語であり、BUILD ファイルの記述方法を定義します。 BUILD ファイルは、シンプルで繰り返しが多いことを想定しています。

すべてのソフトウェアは、記述されるよりも読み取られることのほうが多くなります。これは Starlark に特に当てはまります。エンジニアは BUILD ファイルを読み取って、ターゲットの依存関係とビルドの詳細を把握します。多くの場合、この読み取りは、急いでいるときや、他のタスクと並行して行われます。そのため、ユーザーが BUILD ファイルをすばやく解析して理解できるように、シンプルさと読みやすさが非常に重要になります。

ユーザーが BUILD ファイルを開いたときに、ファイル内のターゲットのリストをすばやく確認したり、C++ ライブラリのソースのリストを確認したり、Java バイナリから依存関係を削除したりしたいと考えます。抽象化のレイヤを追加するたびに、ユーザーがこれらのタスクを実行することが難しくなります。

BUILD ファイルは、さまざまなツールによって分析および更新されます。抽象化を使用している場合、ツールで BUILD ファイルを編集できないことがあります。BUILD ファイルをシンプルに保つことで、より優れたツールを利用できます。コードベースが大きくなるにつれて、ライブラリを更新したり、クリーンアップを行ったりするために、多くの BUILD ファイルに変更を加えることがますます頻繁になります。

一般的なアドバイス

スタイル

Python スタイル

不明な場合は、可能な限り PEP 8 スタイルガイドに従ってください。 特に、Python の慣例に従って、インデントには 2 つではなく 4 つのスペースを使用します。

Starlark は Python ではないため、Python スタイルのいくつかの側面は適用されません。たとえば、PEP 8 では、シングルトンとの比較は is で行うことを推奨していますが、これは Starlark の演算子ではありません。

docstring

docstring を使用してファイルと関数を文書化します。.bzl ファイルの先頭に docstring を使用し、各公開関数に docstring を使用します。

ルールとアスペクトを文書化する

ルールとアスペクト、それらの属性、プロバイダとそのフィールドは、doc 引数を使用して文書化する必要があります。

命名規則

  • 変数名と関数名には、小文字を使用し、単語をアンダースコア([a-z][a-z0-9_]*)で区切ります(例: cc_library)。
  • トップレベルの非公開値はアンダースコアで始まります。Bazel では、非公開値を他のファイルから使用することはできません。ローカル変数にはアンダースコアの接頭辞を使用しないでください。

線の長さ

BUILD ファイルと同様に、ラベルが長くなる可能性があるため、厳密な行の長さの制限はありません。可能な限り、1 行あたり 79 文字以下にしてください(Python の スタイルガイド PEP 8に準拠)。このガイドラインは厳密に適用しないでください。エディタには 80 列以上を表示する必要があります。自動変更では長い行が頻繁に挿入されます。人間はすでに読みやすい行を分割するのに時間を費やすべきではありません。

キーワード引数

キーワード引数では、等号の前後にスペースを入れることをおすすめします。

def fct(name, srcs):
    filtered_srcs = my_filter(source = srcs)
    native.cc_library(
        name = name,
        srcs = filtered_srcs,
        testonly = True,
    )

ブール値

ブール値(ルールでブール属性を使用する場合など)には、10 ではなく、TrueFalse の値を使用することをおすすめします。

本番環境のコードで print() 関数を使用しないでください。これはデバッグ専用であり、.bzl ファイルの直接ユーザーと間接ユーザーすべてにスパムを送信します。唯一の例外は、デフォルトで無効になっていて、ソースを編集してのみ有効にできる print() を使用するコードを送信する場合です。たとえば、print() のすべての使用箇所が if DEBUG: で保護されていて、DEBUGFalse にハードコードされている場合です。これらのステートメントが、読みやすさへの影響を正当化するのに十分なほど有用かどうかを考慮してください。

マクロ

マクロは、読み込みフェーズで 1 つ以上のルールをインスタンス化する関数です。一般に、可能な限りマクロではなくルールを使用してください。ユーザーに表示されるビルドグラフは、ビルド中に Bazel が使用するグラフと同じではありません。マクロは、Bazel がビルドグラフ分析を行う前に展開されます。

このため、問題が発生した場合、ユーザーはマクロの実装を理解してビルドの問題をトラブルシューティングする必要があります。また、bazel query の結果に表示されるターゲットはマクロの展開によるものであるため、結果を解釈するのが難しい場合があります。最後に、アスペクトはマクロを認識しないため、アスペクトに依存するツール(IDE など)が失敗する可能性があります。

マクロを安全に使用できるのは、Bazel CLI または BUILD ファイルで直接参照される追加のターゲットを定義する場合です。この場合、これらのターゲットのエンドユーザーのみがターゲットについて知っておく必要があり、マクロによって発生するビルドの問題は、その使用から遠く離れることはありません。

生成されたターゲットを定義するマクロ(CLI で参照したり、そのマクロでインスタンス化されていないターゲットに依存したりしないマクロの実装の詳細)については、次のベスト プラクティスに従ってください。

  • マクロは name 引数を受け取り、その名前のターゲットを定義する必要があります。 そのターゲットがマクロのメインターゲットになります。
  • 生成されたターゲット(マクロで定義された他のすべてのターゲット)は、次の条件を満たす必要があります。
    • 名前の接頭辞が <name> または _<name> である。たとえば、 name = '%s_bar' % (name) を使用します。
    • 可視性が制限されている(//visibility:private)。
    • ワイルドカード ターゲット(:all...:* など)での展開を避けるために manual タグがある。
  • name は、マクロで定義されたターゲットの名前を派生させるためにのみ使用し、他の目的には使用しないでください。たとえば、マクロ自体で生成されない依存関係や入力ファイルを派生させるために名前を使用しないでください。
  • マクロで作成されたすべてのターゲットは、何らかの方法でメインターゲットに結合する必要があります。
  • 慣例として、マクロを定義するときは、name を最初の引数にする必要があります。
  • マクロのパラメータ名を一貫して使用します。パラメータが属性値としてメインターゲットに渡される場合は、名前を同じにします。マクロ パラメータが deps などの共通ルール属性と同じ目的で使用される場合は、属性と同じ名前を付けます(下記を参照)。
  • マクロを呼び出すときは、キーワード引数のみを使用します。これはルールと一貫しており、読みやすさが大幅に向上します。

エンジニアは、関連するルールの Starlark API が特定のユースケースに十分でない場合に、ルールが Bazel 内でネイティブコードで定義されているか、Starlark で定義されているかに関係なく、マクロを作成することがよくあります。この問題が発生した場合は、ルール作成者に API を拡張して目標を達成できるかどうかを尋ねてください。

経験則として、マクロがルールに似ているほど、結果は良くなります。

マクロもご覧ください。

ルール

  • ルール、アスペクト、それらの属性には、lower_case 名(スネークケース)を使用する必要があります。
  • ルール名は、依存関係(またはリーフ ルールの場合はユーザー)の観点から、ルールによって生成されるアーティファクトの主な種類を表す名詞です。これは必ずしもファイル接尾辞ではありません。たとえば、Python 拡張機能として使用される C++ アーティファクトを生成するルールは py_extension と呼ばれます。ほとんどの言語では、一般的なルールは次のとおりです。
    • *_library - コンパイル単位または「モジュール」。
    • *_binary - 実行可能ファイルまたはデプロイ単位を生成するターゲット。
    • *_test \- テスト ターゲット。これには複数のテストを含めることができます。*_test ターゲットのすべてのテストは、同じテーマのバリエーションであると想定してください(たとえば、単一のライブラリのテスト)。
    • *_import: コンパイル中に使用されるプリコンパイル済みアーティファクト(.jar.dll など)をカプセル化するターゲット。
  • 属性には一貫した名前と型を使用します。一般的に適用可能な属性には、次のものがあります。
    • srcs: label_list。ファイル(ソースファイル、通常は人間が作成)を許可します。
    • deps: label_list。通常、ファイルを許可しません。コンパイルの依存関係。
    • data: label_list。ファイル(テストデータなどのデータファイル)を許可します。
    • runtime_deps: label_list: コンパイルに必要ないランタイムの依存関係。
  • 動作が明確でない属性(特別な置換を含む文字列テンプレートや、特定の要件で呼び出されるツールなど)については、属性の宣言(attr.label_list() など)に doc キーワード引数を使用してドキュメントを提供します。
  • ルール実装関数は、ほぼ常に非公開関数(先頭にアンダースコアが付いた名前)にする必要があります。一般的なスタイルは、 実装関数に myrule という名前を付けることです。_myrule_impl
  • 明確に定義された プロバイダ インターフェースを使用して、ルール間で情報を渡します。プロバイダ フィールドを宣言して文書化します。
  • 拡張性を考慮してルールを設計します。他のルールがルールとやり取りし、プロバイダにアクセスし、作成したアクションを再利用する可能性があることを考慮してください。
  • ルールでパフォーマンスに関するガイドラインに従います。