要約
- 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.
原因
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 文字制約 に引っかかることがある。
解決方法
実行用スクリプトファイルを生成した後、クラスパス指定に関する行をディレクトリ参照 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\*
...
この設定を加えることで、エラーが解消できた。
(別の解決方法として、fatjar を作成してその fatjar のみをクラスパスで参照するよう書き換えれば良いかとも思いましたが、lib
ディレクトリ内のその他の依存関係を排除する方法が分からなかったので、そちらの方法は断念しました)