Gradle Applicationプラグインで生成したBatファイルがcmd.exeの文字列制限に引っかかる時の回避策

2021/06/19に公開

要約

  • Windows の cmd.exe の制約により、Gradle の Application プラグインで生成した Bat ファイルが実行不可能になることがあった
  • Application プラグインが出力する Bat ファイル内でのクラスパスの指定を、jar ファイル指定でなくディレクトリ指定となるよう設定を追加することにより回避可能となった
  • 検証用プロジェクト: https://github.com/kaakaa/gradle-application-too-long-input-line

背景

Gradle の Application プラグインを利用すると、CLI ツールとして作成した Java アプリを実行するためのスクリプトを生成することができる。

plugins {
    id 'application'
}

application.mainClassName = 'sample.Main'

main メソッドを持つクラスを application.mainClassName として指定し、Gradle Application プラグインinstallDist タスクを実行することで、$buildDir/install/${APPLICATION_NAME} 配下に実行可能な形式のアプリケーションが出力される。

$ ./gradlew installDist
$ tree build/install/
build/install/
└── app
    ├── bin
    │   ├── app
    │   └── app.bat
    └── lib
        ├── deps.jar
        ...

bin ディレクトリ配下には Unix 実行用のスクリプトファイルと Windows 実行用の BAT ファイルが出力される。アプリケーションが依存するライブラリは lib 配下にまとめられ、実行用スクリプトファイルから lib 配下の依存ライブラリが参照されるようになっている。

問題

Windows 環境において、Application プラグインから出力された BAT ファイルを実行すると、エラーとなることがあった。

> build/install/gradle-application-too-long-input-line/bin/gradle-application-too-long-input-line.bat
The input line is too long.
The syntax of the command is incorrect.

GitHub Actions での実行結果

原因

Windows の cmd.exe では1行が8191 文字を超えるようなコマンドは実行できない。

コマンド プロンプト (cmd.exe) のコマンドライン文字列の制限

Microsoft Windows XP 以降の Windows を実行しているコンピューターでは、コマンド プロンプトで使用できる文字列の最大長は 8191 文字です。

Gradle の Application プラグインは、実行スクリプトファイル内で依存ライブラリを set CLASSPATH=%APP_HOME%\lib\deps1.jar;%APP_HOME%\lib\deps2.jar;... のように jar ファイル単位でクラスパス文字列に結合しているため、依存ライブラリが多すぎると 8191 文字制約 に引っかかることがある。

サンプル BAT ファイル

解決方法

実行用スクリプトファイルを生成した後、クラスパス指定に関する行をディレクトリ参照 1 つで済むように文字列置換することでこのエラーは回避可能。

...
tasks.withType(CreateStartScripts).each { task ->
    task.doLast {
        String text = task.windowsScript.text
        text = text.replaceFirst(/(set CLASSPATH=%APP_HOME%\\lib\\).*/, { "${it[1]}*" })
        task.windowsScript.write text
    }
}
...

参考: Workaround for gradle application plugin 'the input line is too long' error on Windows

上記の設定を build.gradle に追加し、再度 BAT ファイルを生成するとエラーとなっていた行は下記のように置換されるため、文字数制限に抵触しなくなる。

...
set CLASSPATH=%APP_HOME%\lib\*
...

サンプル BAT ファイル

この設定を加えることで、エラーが解消できた。

GitHub Actions での実行結果


(別の解決方法として、fatjar を作成してその fatjar のみをクラスパスで参照するよう書き換えれば良いかとも思いましたが、libディレクトリ内のその他の依存関係を排除する方法が分からなかったので、そちらの方法は断念しました)

Discussion