Windows でのルールの記述

7.3 · 7.2 · 7.1 · 7.0 · 6.5

このページでは、Windows 互換のルールの作成、ポータブル ルールの作成に関する一般的な問題、およびいくつかの解決策について説明します。

パス

問題:

  • 長さの上限: パスの最大長は 259 文字です。

    Windows では長いパス(最大 32,767 文字)もサポートされていますが、多くのプログラムは下限でビルドされています。

    アクションで実行するプログラムについては、この点に注意してください。

  • 作業ディレクトリ: 259 文字以内で指定します。

    プロセスは、259 文字を超えるディレクトリに cd できません。

  • 大文字と小文字の区別: Windows のパスでは大文字と小文字が区別されません。Unix のパスでは大文字と小文字が区別されます。

    アクションのコマンドラインを作成する際は、この点に注意してください。

  • パス区切り文字: バックスラッシュ(\`), not forward slash (/)です。

    Bazel は、/ セパレータを使用して Unix スタイルでパスを保存します。Unix スタイルのパスをサポートする Windows プログラムもあれば、サポートしていないプログラムもあります。cmd.exe の組み込みコマンドによっては、これらのコマンドをサポートするものとサポートしないものがあります。

    アクションのコマンドラインと環境変数を作成する場合は、常に \` separators on Windows: replace/with を使用することをおすすめします。

  • 絶対パス: 先頭をスラッシュ(/)にしないでください。

    Windows の絶対パスは、C:\foo\bar.txt などのドライブ文字で始まります。単一のファイル システム ルートはありません。

    ルールでパスが絶対パスかどうかを確認する場合は、この点に注意してください。絶対パスはポータブルでないことが多いため、使用しないでください。

ソリューション:

  • パスは短くします。

    長いディレクトリ名、ネスト構造の深いディレクトリ構造、長いファイル名、長いワークスペース名、長いターゲット名は避けてください。

    これらはすべて、アクションの入力ファイルのパス コンポーネントになり、パスの長さの上限を超える可能性があります。

  • 短い出力ルートを使用する。

    Bazel 出力の短いパスを指定するには、--output_user_root=<path> フラグを使用します。Bazel の出力専用のドライブ(または仮想ドライブ)を用意することをおすすめします(D:\`), and adding this line to your.bazelrc ファイルなど)。

    build --output_user_root=D:/
    

    または

    build --output_user_root=C:/_bzl
    
  • ジャンクションを使用する。

    ジャンクションは、大まかに言えば[1]、ディレクトリ シンボリック リンクです。ジャンクションは簡単に作成でき、長いパスのディレクトリ(同じコンピュータ上)を参照できます。ビルド アクションで、パスは短いがターゲットが長いジャンクションを作成した場合、短いパスの上限があるツールは、ジャンクションされたディレクトリ内のファイルにアクセスできます。

    .bat ファイルまたは cmd.exe で、次のようにジャンクションを作成できます。

    mklink /J c:\path\to\junction c:\path\to\very\long\target\path
    

    [1]: 厳密に言えば、ジャンクションはシンボリック リンクではありませんが、ビルド アクションのために、ジャンクションをディレクトリ シンボリック リンクと見なすことができます。

  • アクション / envvars のパス内の / を `` に置き換える。

    アクションのコマンドラインまたは環境変数を作成するときは、パスを Windows スタイルにします。例:

    def as_path(p, is_windows):
        if is_windows:
            return p.replace("/", "\\")
        else:
            return p
    

環境変数

問題:

  • 大文字と小文字の区別: Windows 環境変数名では、大文字と小文字が区別されません。

    たとえば、Java では System.getenv("SystemRoot")System.getenv("SYSTEMROOT") は同じ結果になります。(これは他の言語にも適用されます)。

  • Hermeticity: アクションでは、カスタム環境変数をできるだけ使用しないでください。

    環境変数は、アクションのキャッシュキーの一部です。アクションで頻繁に変更される環境変数やユーザー固有の環境変数を使用すると、ルールをキャッシュに保存しにくくなります。

ソリューション:

  • 環境変数名はすべて大文字で使用します。

    これは Windows、macOS、Linux で動作します。

  • アクション環境を最小限に抑える。

    ctx.actions.run を使用する場合は、環境を ctx.configuration.default_shell_env に設定します。アクションにさらに環境変数が必要であれば、それらをすべて辞書に入れてアクションに渡します。例:

    load("@bazel_skylib//lib:dicts.bzl", "dicts")
    
    def _make_env(ctx, output_file, is_windows):
        out_path = output_file.path
        if is_windows:
            out_path = out_path.replace("/", "\\")
        return dicts.add(ctx.configuration.default_shell_env, {"MY_OUTPUT": out_path})
    

操作

問題:

  • 実行可能出力: すべての実行可能ファイルには実行可能拡張子が必要です。

    最も一般的な拡張子は .exe(バイナリ ファイル)と .bat(バッチ スクリプト)です。

    シェル スクリプト(.sh)は Windows では実行できないため、ctx.actions.runexecutable として指定することはできません。また、ファイルに付与できる +x 権限もないため、Linux のように任意のファイルを実行することはできません。

  • Bash コマンド: ポータビリティを確保するため、アクションで Bash コマンドを直接実行しないでください。

    Bash は Unix 系のシステムに広く使用されていますが、Windows では使用できないことがよくあります。Bazel 自体は Bash(MSYS2)に依存しなくなってきており、今後は Bazel と一緒に MSYS2 をインストールする必要は少なくなります。Windows でルールを簡単に使用できるようにするには、アクションで Bash コマンドを実行しないでください。

  • 改行: Windows では CRLF(\r\n)が使用され、Unix 系システムでは LF(\n)が使用されます。

    テキスト ファイルを比較する際は、この点に注意してください。Git の設定に注意してください。特に、チェックアウトや commit 時の行末に注意してください。(Git の core.autocrlf 設定をご覧ください)。

ソリューション:

  • Bash を使用しない目的別のルールを使用する。

    native.genrule() は Bash コマンドのラッパーであり、ファイルのコピーやテキスト ファイルの書き込みなどの簡単な問題の解決によく使用されます。Bash に依存せず(そして車輪の再発明をせずに)、bazel-skylib にニーズに合った専用のルールがあるかどうかを確認できます。いずれも、Windows でビルドまたはテストするときに Bash に依存しません。

    ビルドルールの例:

    • copy_file()出典ドキュメント): ファイルを別の場所にコピーし、必要に応じて実行可能にします。

    • write_file()ソースドキュメント): テキスト ファイルを書き込み、必要な行末(autounixwindows)を指定します。必要に応じて、実行可能にします(スクリプトの場合)。

    • run_binary()ソースドキュメント): 指定された入力と期待される出力を持つバイナリ(または *_binary ルール)をビルドアクションとして実行します(これは ctx.actions.run のビルドルール ラッパーです)。

    • native_binary()ソースドキュメント): ネイティブ バイナリを *_binary ルールでラップします。このルールは、bazel run として使用することも、run_binary()tool 属性または native.genrule()tools 属性で使用することもできます。

    テストルールの例:

  • Windows では、些細なことのために .bat スクリプトを使用することを検討してください。

    .sh スクリプトの代わりに、.bat スクリプトを使用して簡単なタスクを解決できます。

    たとえば、何もしないスクリプト、メッセージを出力するスクリプト、固定のエラーコードで終了するスクリプトなどが必要であれば、シンプルな .bat ファイルで十分です。ルールが DefaultInfo() プロバイダを返す場合、executable フィールドは Windows 上の .bat ファイルを参照する可能性があります。

    macOS と Linux ではファイル拡張子に関係がないため、シェル スクリプトでも拡張子として .bat を使用できます。

    空の .bat ファイルは実行できません。空のスクリプトが必要であれば、1 つのスペースを記述します。

  • 原則に基づいて Bash を使用する。

    Starlark のビルドルールとテストルールでは、ctx.actions.run_shell を使用して Bash スクリプトと Bash コマンドをアクションとして実行します。

    Starlark マクロでは、Bash スクリプトとコマンドを native.sh_binary() または native.genrule() でラップします。Bazel は、Bash が使用可能かどうかを確認し、Bash でスクリプトまたはコマンドを実行します。

    Starlark リポジトリ ルールでは、Bash を完全に回避してください。現在のところ、Bazel では、リポジトリ ルールで原則に沿って Bash コマンドを実行する方法はありません。

ファイルの削除

問題:

  • ファイルを開いている間は削除できません。

    開いているファイルは(デフォルトでは)削除できず、試行すると「アクセス拒否」エラーが発生します。ファイルを削除できない場合、実行中のプロセスによってファイルが開いたままになっている可能性があります。

  • 実行中のプロセスの作業ディレクトリは削除できません。

    プロセスには作業ディレクトリへのオープン ハンドルがあり、プロセスが終了するまでディレクトリを削除できません。

ソリューション:

  • コードで、ファイルを積極的に閉じるようにします。

    Java では、try-with-resources を使用します。Python では、with open(...) as f: を使用します。原則として、ハンドルはできるだけ早く閉じるようにしてください。