サンプルコードで身につける: Gradle: タスクの依存関係と、変更が無ければ飛ばす仕組み
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1実行せずに順番だけ見る: build --dry-run と help --task
(-m)は「all task actions disabled」で走り、どのがどの順で実行されるはずだったかを SKIPPED 付きで出します。雛形の build は13行でした。 は、そのタスクを登録した、型、説明を出します。bootJar は プラグインが登録した BootJar 型で、build は LifecycleBasePlugin の素の Task 型(自分では何もしない束ね役)です。
:compileJava SKIPPED:processResources SKIPPED:classes SKIPPED:resolveMainClassName SKIPPED:bootJar SKIPPED:jar SKIPPED:assemble SKIPPED:compileTestJava SKIPPED:processTestResources SKIPPED:testClasses SKIPPED:test SKIPPED:check SKIPPED:build SKIPPEDBUILD SUCCESSFUL in 349ms$ ./gradlew -q help --task bootJarDetailed task information for bootJarPath:bootJar (registered by plugin 'org.springframework.boot')TypeBootJar (org.springframework.boot.gradle.tasks.bundling.BootJar)Options--rerun Causes the task to be re-run even if up-to-date.DescriptionAssembles an executable jar archive containing the main classes and their dependencies.Groupbuild$ ./gradlew -q help --task buildDetailed task information for buildPath:build (registered by plugin class 'org.gradle.language.base.plugins.LifecycleBasePlugin')TypeTask (org.gradle.api.Task)...DescriptionAssembles and tests this project.
22回目は UP-TO-DATE、1ファイル変えると必要な所だけ再実行
何も変えずに build すると、13行のうち12行が ( はテスト用が無いので )で、7 actionable tasks: 7 up-to-date でした。HelloController.java の文字列を1か所変えると compileJava から再実行され、processResources と compileTestJava は UP-TO-DATE のままです(入力が変わっていないので)。test は、テストのソースは同じでも上の が変わったので再実行されました。 を付けると、その理由が Input property ... has changed. の形で出ます(3つ目は別の1か所変更で --info を付けた回の抜粋です)。
> Task :compileJava UP-TO-DATE> Task :processResources UP-TO-DATE> Task :classes UP-TO-DATE> Task :resolveMainClassName UP-TO-DATE> Task :bootJar UP-TO-DATE> Task :jar UP-TO-DATE> Task :assemble UP-TO-DATE> Task :compileTestJava UP-TO-DATE> Task :processTestResources NO-SOURCE> Task :testClasses UP-TO-DATE> Task :test UP-TO-DATE> Task :check UP-TO-DATE> Task :build UP-TO-DATEBUILD SUCCESSFUL in 343ms7 actionable tasks: 7 up-to-date$ sed -i 's/"hello")/"hello2")/' src/main/java/com/example/demo/HelloController.java; ./gradlew --console=plain build> Task :compileJava> Task :processResources UP-TO-DATE> Task :classes> Task :resolveMainClassName> Task :bootJar> Task :jar> Task :assemble> Task :compileTestJava UP-TO-DATE> Task :processTestResources NO-SOURCE> Task :testClasses UP-TO-DATE> Task :test> Task :check> Task :buildBUILD SUCCESSFUL in 1s7 actionable tasks: 5 executed, 2 up-to-date$ sed -i 's/"hello")/"hello3")/' src/main/java/com/example/demo/HelloController.java; ./gradlew --console=plain build --info | grep -i 'is not up-to-date\|Input property\|has changed\|Task :compileJava\|Task :bootJar\|Task :test\|Task :jar\|Task :processResources'> Task :compileJavaTask ':compileJava' is not up-to-date because:Input property 'stableSources' file /work/groovy/src/main/java/com/example/demo/HelloController.java has changed.> Task :processResources UP-TO-DATE...> Task :bootJarTask ':bootJar' is not up-to-date because:Input property 'classpath' file /work/groovy/build/classes/java/main/com/example/demo/HelloController.class has changed....> Task :testTask ':test' is not up-to-date because:Input property 'stableClasspath' file /work/groovy/build/classes/java/main/com/example/demo/HelloController.class has changed.
3clean しても戻ってくる FROM-CACHE と、構成の再利用、デーモン
を付けると、タスクの出力が ~/.gradle/caches/build-cache-1 にも保存されます。 で を消してからもう一度 clean build --build-cache を打つと、compileJava・compileTestJava・test が実行されずに で戻りました(bootJar・・processResources は実行されました。この違いの理由は文書で確かめていません)。 は2回目に Reusing configuration cache. と出ます。は で見え、 で止まります。
> Task :clean> Task :compileJava FROM-CACHE> Task :processResources> Task :classes> Task :resolveMainClassName> Task :bootJar> Task :jar> Task :assemble> Task :compileTestJava FROM-CACHE> Task :processTestResources NO-SOURCE> Task :testClasses UP-TO-DATE> Task :test FROM-CACHE> Task :check UP-TO-DATE> Task :buildBUILD SUCCESSFUL in 413ms8 actionable tasks: 5 executed, 3 from cache$ ./gradlew --console=plain build --configuration-cache # 1回目Calculating task graph as no cached configuration is available for tasks: build...Configuration cache entry stored.$ ./gradlew --console=plain build --configuration-cache # 2回目Reusing configuration cache....Configuration cache entry reused.$ ./gradlew --statusPID STATUS INFO117 IDLE 9.7.1$ ./gradlew --stopStopping Daemon(s)1 Daemon stopped$ ./gradlew --statusNo Gradle daemons are running.PID STATUS INFO117 STOPPED (stop command received)...
4build/ に出来たものと、2つの JAR の違い
build/ の中身です。classes/java/main は の に当たりますが、java/main の2段が深くなっています。libs には が2つあり、 は jar タスクが作る自分の2個とリソース( と空の )だけのもの(2,065 B)で、も Manifest-Version の1行だけです。demo-0.0.1-SNAPSHOT.jar は bootJar が作った で、マニフェストに と があり、 に の34本が入っています。
build/classes/java/main/com/example/demo/DemoApplication.classbuild/classes/java/main/com/example/demo/HelloController.classbuild/classes/java/test/com/example/demo/DemoApplicationTests.classbuild/libs/demo-0.0.1-SNAPSHOT.jarbuild/libs/demo-0.0.1-SNAPSHOT-plain.jarbuild/reports/tests/test/com.example.demo.DemoApplicationTests/contextLoads().htmlbuild/reports/tests/test/com.example.demo.DemoApplicationTests/index.html...build/reports/tests/test/index.html...build/resolvedMainClassNamebuild/resources/main/application.properties...build/test-results/test/TEST-com.example.demo.DemoApplicationTests.xml$ ls -l build/libs-rw-r--r-- 1 root root 2065 Oct 3 00:31 demo-0.0.1-SNAPSHOT-plain.jar-rw-r--r-- 1 root root 19903503 Oct 3 00:31 demo-0.0.1-SNAPSHOT.jar$ unzip -p build/libs/demo-0.0.1-SNAPSHOT-plain.jar META-INF/MANIFEST.MFManifest-Version: 1.0$ unzip -p build/libs/demo-0.0.1-SNAPSHOT.jar META-INF/MANIFEST.MFManifest-Version: 1.0Main-Class: org.springframework.boot.loader.launch.JarLauncherStart-Class: com.example.demo.DemoApplicationSpring-Boot-Version: 4.1.1Spring-Boot-Classes: BOOT-INF/classes/Spring-Boot-Lib: BOOT-INF/lib/Spring-Boot-Classpath-Index: BOOT-INF/classpath.idxSpring-Boot-Layers-Index: BOOT-INF/layers.idxBuild-Jdk-Spec: 25Implementation-Title: demoImplementation-Version: 0.0.1-SNAPSHOT
5よくある誤解: touch すれば再ビルドされる、コメントだけなら何も起きない
どちらも では成り立ちません。touch で更新時刻だけ変えても、入力の内容が同じなので UP-TO-DATE のままでした(NO-SOURCE の1行を除く12行)。逆に、クラスの前にコメントを1行足しただけでも compileJava が走り、行番号が変わって .class の中身が変わるので、bootJar・jar・test まで再実行されました。sed で足したコメントの文言は実験前の予想で、結果はその逆です。対になるのが の タスクで、更新時刻で判定するので touch だけで再コンパイルします。
$ touch src/main/java/com/example/demo/HelloController.java src/main/resources/application.properties; ./gradlew --console=plain build> Task :compileJava UP-TO-DATE> Task :processResources UP-TO-DATE...(残りも UP-TO-DATE。processTestResources だけ NO-SOURCE)7 actionable tasks: 7 up-to-date$ sed -i 's|^@RestController|// a comment that does not change the bytecode\n@RestController|' src/main/java/com/example/demo/HelloController.java; ./gradlew --console=plain build# コメントの文言は実験前の予想。実際は行番号がずれて .class が変わり、bootJar・test まで再実行された> Task :compileJava> Task :processResources UP-TO-DATE> Task :classes> Task :resolveMainClassName> Task :bootJar> Task :jar> Task :assemble> Task :compileTestJava UP-TO-DATE> Task :processTestResources NO-SOURCE> Task :testClasses UP-TO-DATE> Task :test> Task :check> Task :buildBUILD SUCCESSFUL in 1s7 actionable tasks: 5 executed, 2 up-to-date# 対照(Ant 1.10.18、別のレッスンの最小 build.xml): touch の後の ant は# [javac] Compiling 1 source file to ... と再コンパイルした(更新時刻で判定)