.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 では、Starlark の演算子ではない is を使用してシングルトンと比較することが推奨されています。

docstring

docstring を使用して、ファイルと関数をドキュメント化します。各 .bzl ファイルの先頭に docstring を使用し、各公開関数に docstring を使用します。

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

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

命名規則

  • 変数名と関数名では、cc_library のように、小文字とアンダースコア([a-z][a-z0-9_]*)で区切られた単語を使用します。
  • 非公開のトップレベルの値は 1 つのアンダースコアで始まります。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 またはビルドファイルで直接参照する追加のターゲットを定義することです。この場合、それらのターゲットのエンドユーザーのみが認識する必要があり、マクロによって発生するビルドの問題は、その使用からそれほど離れていません。

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

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

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

経験則では、ルールに近いマクロが多いほど、より良い結果が得られます。

マクロもご覧ください。

ルール

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