@opengl-8080 さんが5年前にQiitaに投稿したGraleに関する記事3本を読んだ。
- Groovyを知らない人のためのbuild.gradle読み書き入門, opengl-8080, QIITA
- Gradleを完全に理解した人が、何も分からなくなるための第一歩, opengl-8080, QIITA
- Gradleのタスク定義のあれこれ, opengl-8080, QIITA
わたしはGradleを何年も使っているけれど、とりあえず動いたで立ち止まっていた。実はbuild.gradleがよく分かっていなかった。今回、@opengl-8080 さんの記事を読んで目から鱗が三枚ぐらい落ちた。
解決したい問題
「Gradleのタスクの定義のあれこれ」 (これを以下で「あれこれ」と略記する)を読んでいて困ったことがありました。「あれこれ」にはGradleビルドスクリプトのサンプルが(なんとまあ)62個掲載されていた。わたしはサンプルコードを見ながらエディタでコードを書きGradleで実行してどのように動作するかを確かめた。「あれこれ」はそれらサンプルに build.gradle
という同一のラベルを付けて掲載していた。しかし中身の違う62個のビルドスクリプトを同じ build.gradle
という名でファイル保存することはできない。ひとつひとつに固有のファイル名を付けて保存するしかない。だからわたしはビルドスクリプトをこんなふうに作った。
ファイル名: 25_fileTree.gradle
task foo {
doFirst {
FileTree tree = fileTree("src")
tree.each { println it }
}
}
この調子でビルドスクリプトを作っていったら、最終的に以下のファイルができた。
01_task_foo.gradle
02_doFist_doLast.gradle
03_configure_task_with_closure.gradle
04_build_lifecycle.gradle
04_task_dependsOn_other.gradle
05_reference_to_task.gradle
06_task_dependsOn_other.gradle
07_task_runs_only_once.gradle
08_dependsOn_inside_configuration_block.gradle
09_dependsOn_as_task_argument.gradle
10_sequence_control_not_good.gradle
11_sequence_control_by_dependsOn.gradle
12_sequence_control_mustRunAfter.gradle
13_finalizedBy.gradle
14_onlyIf.gradle
15_onlyIf_with_finalizedBy.gradle
21_file.gradle
22_files.gradle
23_files_asPath.gradle
24_files_filter.gradle
25_fileTree.gradle
26_fileTree_include_as_closure.gradle
27_fileTree_include_as_Map.gradle
31_task_without_inputs_outputs.gradle
32_task_with_inputs_outputs.gradle
33_task_inputs_declaration.gradle
34_task_outputs_declaration.gradle
41_tasks_addRule.gradle
42_clean_rule.gradle
51_custom_task_within_buildscript.gradle
52_custom_task_input.gradle
53_custom_task_input_as_Object.gradle
54_annotations_for_Input_Output.gradle
61_Copy_task.gradle
62_Copy_from_Object.gradle
63_Copy_from_Object_Closure.gradle
64_Copy_into_Object.gradle
65_Copy_include.gradle
66_Copy_exclude.gradle
67_Copy_rename_closure.gradle
68_Copy_rename_String_String.gradle
69_copy_method.gradle
71_Delete_task.gradle
72_Delete_fileTree.gradle
73_delete_method_of_Project.gradle
74_Sync_task.gradle
75_Sync_preserve.gradle
76_sync_method_of_Project.gradle
77_Exec_task.gradle
78_exec_method_of_Project.gradle
79_Zip_task.gradle
80_zipTree.gradle
81_unzip.gradle
85_buildSrc.gradle
86_Plugin.gradle
87_Plugin_with_Extension.gradle
88_Plugin_with_Extension_configured_by_closure.gradle
89_Plugin_with_Extension_inputFile=null_outputDir=null.gradle
90_Plugin_with_Extension_using_Property.gradle
91_CustomCopyTask.gradle
さて、これらビルドスクリプトをgradleで実行したいが、コマンドラインでどのように入力するか?
ひとつのやり方は --build-file
オプションでファイル名を指定すること。たとえばコマンドラインで次のようにキー入力すればいい。
$ ./gradlew -b 25_fileTree.gradle foo
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :foo
/Users/.../src/main/resources/hoge/hoge.xml
/Users/.../src/main/resources/main.xml
/Users/.../src/main/resources/fuga/fuga.xml
/Users/.../src/main/java/hoge/Hoge.java
/Users/.../src/main/java/fuga/Fuga.java
/Users/.../src/main/java/Main.java
これでちゃんと動きました。でも、繰り返しgradleを起動するたびに長いファイル名をキー入力するのが面倒だった。なんとかしたい。
解決方法
シェルスクリプト grx
を作りました。
ファイル名: grx
#!/bin/sh
#f='01_task_foo.gradle'
#f='02_doFist_doLast.gradle'
#f='03_configure_task_with_closure.gradle'
#f='04_build_lifecycle.gradle'
#f='05_reference_to_task.gradle'
#f='06_task_dependsOn_other.gradle'
#f='07_task_runs_only_once.gradle'
#f='08_dependsOn_inside_configuration_block.gradle'
#f='09_dependsOn_as_task_argument.gradle'
#f='10_sequence_control_not_good.gradle'
#f='11_sequence_control_by_dependsOn.gradle'
#f='12_sequence_control_mustRunAfter.gradle'
#f='13_finalizedBy.gradle'
#f='14_onlyIf.gradle'
#f='15_onlyIf_with_finalizedBy.gradle'
#f='21_file.gradle'
#f='22_files.gradle'
#f='23_files_asPath.gradle'
#f='24_files_filter.gradle'
f='25_fileTree.gradle'
#f='26_fileTree_include_as_closure.gradle'
#f='27_fileTree_include_as_Map.gradle'
#f='31_task_without_inputs_outputs.gradle'
#f='32_task_with_inputs_outputs.gradle'
#f='33_task_inputs_declaration.gradle'
#f='34_task_outputs_declaration.gradle'
#f='41_tasks_addRule.gradle'
#f='42_clean_rule.gradle'
#f='51_custom_task_within_buildscript.gradle'
#f='52_custom_task_input.gradle'
#f='53_custom_task_input_as_Object.gradle'
#f='54_annotations_for_Input_Output.gradle'
#f='61_Copy_task.gradle'
#f='62_Copy_from_Object.gradle'
#f='63_Copy_from_Object_Closure.gradle'
#f='64_Copy_into_Object.gradle'
#f='65_Copy_include.gradle'
#f='66_Copy_exclude.gradle'
#f='67_Copy_rename_closure.gradle'
#f='68_Copy_rename_String_String.gradle'
#f='69_copy_method.gradle'
#f='71_Delete_task.gradle'
#f='72_Delete_fileTree.gradle'
#f='73_delete_method_of_Project.gradle'
#f='74_Sync_task.gradle'
#f='75_Sync_preserve.gradle'
#f='76_sync_method_of_Project.gradle'
#f='77_Exec_task.gradle'
#f='78_exec_method_of_Project.gradle'
#f='79_Zip_task.gradle'
#f='80_zipTree.gradle'
#f='81_unzip.gradle'
#f='85_buildSrc.gradle'
#f='86_Plugin.gradle'
#f='87_Plugin_with_Extension.gradle'
#f='88_Plugin_with_Extension_configured_by_closure.gradle'
#f='89_Plugin_with_Extension_inputFile=null_outputDir=null.gradle'
#f='90_Plugin_with_Extension_using_Property.gradle'
#f='91_CustomCopyTask.gradle'
cp $f build.gradle
./gradlew -q $1 $2 $3 $4 $5 $6 $7 $8 $9
grx
が何をしているかは一目瞭然です。変数f
が示すファイルの中身を build.gradle
に上書きしてから gradlew
コマンドを起動する。それだけ。コマンドラインでこんなふうに grx
を利用します。
$ grx foo
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :foo
/Users/.../src/main/resources/hoge/hoge.xml
...
grx
がどのビルドファイルを実行するか?それは grx
のコードを都度修正して切り替えます。シェル変数 f
にファイル名を代入する行を取り替える。grx
のコードにビルドファイルの正しいファイル名をいちど列挙してしまえば、あとは行の先頭のコメント記号 #
を付けたり外したりすればいい。
わたしの意見
grx
を作って何が嬉しかったか。Gradleのビルド・スクリプトのファイル名を build.gradle
ではない任意の名前にしても構わないという自由が得られたこと。
というのも、Gradleのビルド・ファイルの名前は build.gradle
しかあり得ないという思い込みがわたしにはあったからだ。この思い込みのせいでビルド・ファイルを2つ以上作ることをためらった。1個の build.gradle
ファイルの中に foo
タスクを作った。次に foo2
タスクを作った。次に foo3
タスクを作り、 foo4
タスクを作った。さらに foo5
タスクを作った。タスク名が重複しないよう気配りしながら。そうこうするうちに build.gradle
ファイルは膨らんでぐちゃぐちゃになり、とても読めたものでなくなった。こんなことしなきゃいけないGradleってどうなのよとムカついた。このやり方でGradleを学習するのはわたしにとって苦痛だった。
ふと、わたしは grx
を思いついた。grxがあれば小分けにしたビルド・スクリプトをいくらでも書くことができる。小分けされた個々のスクリプトが同じタスク名(たとえばfoo
)を繰り返し使っていても問題ない。grxが小分けされたスクリプトをひとつ拾ってbuild.gradleを生成するから実行時にタスク名の重複は起きない。また、ファイル名が 89_Plugin_with_Extension_inputFile=null_outputDir=null.gradle
のように長くても問題ないという点もナイスだ。ファイル名をシェル・スクリプトの中に一度書いておけばよくて、コマンドラインで長いファイル名を繰り返しタイプする手間を無くすることができる。grx
のおかげでGradleビルド・スクリプトをたくさん書くことが苦でなくなった。むしろ楽しくなった。
もっとも、Javaアプリケーションやライブラリを開発することを目的とするプロジェクトにおいてGradleビルド・スクリプトを沢山たくさん書く必要などない。build.gradleをひとつ書いておしまいだ。ところがGradleそのものを学ぼうと志してチュートリアルを読むことを想像してごらんなさい。たとえば
とか。この記事にはbuild.gradleのexampleが48本も掲載されている。チュートリアルを読むのに grx
のようなシェル芸がきっと役に立つだろう。