.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 を使用してファイルと関数をドキュメント化する。各 .bzl ファイルの先頭に docstring を使用し、公開関数ごとに docstring を使用します。

ルールと側面を文書化する

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

命名規則

  • 変数名と関数名では小文字を使用し、単語をアンダースコア([a-z][a-z0-9_]*)で区切る(例: cc_library)。
  • トップレベルのプライベート値は 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 または BUILD ファイルで直接参照する追加のターゲットを定義する場合は、マクロを安全に使うことができます。その場合、これらのターゲットのエンドユーザーのみがターゲットを認識する必要があり、マクロによって生じるビルドの問題もその使用に大きく影響します。

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

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

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

原則として、ルールに近いマクロは多いほど効果的です。

「マクロ」もご覧ください。

ルール

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