コード例
Windows / PowerShellKotlin DSLを全章へ反映

ENGINEERING NOTEBOOK / 03 GRADLE ADVANCED

ビルドを理解し、
設計し、改善する。

動かせるようになった、その次へ。
同じプロジェクトに小さな改善を重ね、設定の理由を説明できるようになる8章です。

基礎編12章を修了し、SampleSystemのapp・commonが動くところから始めます。Javaのソースは引き継ぎ、今度はビルドそのものを育てます。

演習用のコピーを用意する

Eclipse 2026-06 + Buildship / JDK 25 / Gradle 9.6.0
更新実験のみ9.8.1。Kotlin DSLを標準表示、Groovy DSLにも対応。

  1. STEP 01Gradleは、いつ設定を読み、いつTaskを実行する?
  2. STEP 02自分でTaskを作りたい
  3. STEP 03ライブラリのバージョンを一か所で管理したい
  4. STEP 04複数プロジェクトの共通設定を整理したい
  5. STEP 05Gradleのビルドを速くしたい
  6. STEP 06別々のGradleビルドを組み合わせたい
  7. STEP 07Gradleのバージョンを更新したい
  8. STEP 08CI/CDと成果物の公開につなげたい

BEFORE YOU START

基礎編の成果物を残して、始める。

基礎編第10章のSampleSystem(appとcommon、テスト1件が成功する状態)を使います。元の成果物を残すため、同じ親フォルダーに空の AdvancedSampleSystem を作ります。以後、第1〜6・8章はこのコピーが作業先です。第7章だけ更新実験用にさらにコピーします。

コピー元:SampleSystem内コピー先:AdvancedSampleSystem内
gradlew、gradlew.bat同名でルートへ
gradle/wrapper/gradle-wrapper.jar、gradle/wrapper/gradle-wrapper.properties同じ相対位置へ
settings.gradle.kts、build.gradle.kts、gradle.properties同名でルートへ
app/build.gradle.kts、app/src/app/へ。テスト用srcも含める
common/build.gradle.kts、common/src/common/へ

OSのエクスプローラーで上のファイル・フォルダーだけをコピーします。各階層の .gradle/、build/、.project、.classpath、.settings/ はコピーしません。gradle/wrapper/ と .gradle/ は別物です。Git関連ファイルもこの演習には不要です。

gradle/libs.versions.toml がSampleSystemにあれば同じ場所へコピーします。基礎編第10章の手順ではコピー対象外だったので、なければ元のSampleAppの gradle/libs.versions.toml をここへコピーしてください。基礎編第1章のinitで生成された、まだ使っていないファイルを第3章で利用します。元ファイルを失った場合だけ、空のファイルを同じ位置に作って構いません。

PowerShell / コピー後、作業先を切り替える
Set-Location 'C:\自分の練習用フォルダー\AdvancedSampleSystem'
Get-Location

パスを自分の配置に置き換えます。現在位置の末尾がAdvancedSampleSystemであることを確認してください。

settings.gradle.ktsコピー側だけをこの全文へ置換
rootProject.name = "AdvancedSampleSystem"
include("app", "common")
PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat --version
.\gradlew.bat projects
.\gradlew.bat :app:build
.\gradlew.bat :app:run

Gradle 9.6.0、ルートAdvancedSampleSystem、app/common、テスト1件成功、Hello, Gradle!を確認します。最初からここで失敗する場合は、基礎編の状態とJDK 25を確認してから先へ進みます。

Eclipseでは基礎編と同じExisting Gradle ProjectのImportで AdvancedSampleSystem を選び、Wrapper・JDK 25を使います。ビルド定義の編集後は保存し、Gradle Refreshで反映します。以降の成否は、IDEの自動ビルド表示ではなく、ここに載せるWrapperコマンドで確かめます。

01

ADVANCED / 同じビルドを育てる

Gradleは、いつ設定を読み、いつTaskを実行する?

01今回やりたいこと

「buildファイルに書いた処理は、上から順に全部実行される?」を確かめます。Taskを頼んでいないのに出るメッセージと、頼んだときだけ出るメッセージを見分けましょう。

02なぜ今の状態では困る?

ファイルのトップレベルの処理と、Taskが仕事をするときの処理を混ぜると、helpを実行するだけでファイルを書き換える、といった意図しない動きにつながります。

03どういう仕組みで解決する?

Initializationsettingsを読み、参加するProjectを決める
ConfigurationProjectを設定し、必要なTaskの実行計画を作る
Execution選ばれたTaskを依存関係に従って処理
矢印はビルド処理の段階。独立したTask同士の実行順序を表していません。

tasks.register はTaskを登録し、設定を必要になるまで遅らせます。登録ブロック内は Taskの設定、doLast の中は Taskの実行時の処理 です。設定が評価されても、そのTaskが実行されるとは限りません。

PHASE LAB / 概念例

ブラウザ内の説明切替です。Gradleは実行しません。

04どのファイルを変更する?

準備を終えたルートの settings.gradle.kts と build.gradle.kts を使います。app/commonは変更しません。

05何を追加・変更する?

settings.gradle.kts既存のincludeの後ろへ1行追加
println("[init] settings")
build.gradle.ktsルートのコメントだけの内容を、この全文へ置換
println("[config] root build")
tasks.register("observe") {
    println("[config] observe")
    doLast { println("[execute] observe") }
}

06実際に動かして確認する

PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat help --no-configuration-cache
.\gradlew.bat observe --no-configuration-cache

1行目はhelpだけを頼みます。[init] settingsと[config] root buildを探し、[execute] observeが出ないことを確認。2行目はobserveを頼み、[config] observeと[execute] observeも出ることを確認します。

場所helpの場合observeの場合
settingsのprintln表示される表示される
buildのトップレベル表示される表示される
register内のprintlnこの構成では表示されないTaskの設定時に表示
doLast内のprintln表示されないTaskの実行時に表示

07何が変わった?

buildファイルは命令をすべて順番に実行する手順書ではありません。Gradleに設定・Task・依存関係を渡し、依頼したTaskの仕事を実行させます。

後片付け:settingsへ足した観察用printlnを削除します。ルートbuildのobserveとprintlnは、第2章の全文置換で取り除きます。

「helpでは設定されない」の範囲

遅延登録したTaskを他の設定からget()などで取り出すと、そこで設定が必要になることがあります。この例の結果を、すべてのPlugin・ビルドに一般化しないでください。第5章ではConfiguration Cacheにより設定全体を再利用する場合も観察します。

次へ進む条件

printlnとdoLastを見て「設定時/実行時」を説明でき、観察用コードを残さず第2章へ進める。

公式資料:Build Lifecycle / Task Configuration Avoidance

02

ADVANCED / 同じビルドを育てる

自分でTaskを作りたい

01今回やりたいこと

ビルドで使う情報を build/info/build-info.properties に出力したい。既存Taskを使う段階から、小さな仕事を自分で定義する段階へ進みます。

02なぜ今の状態では困る?

単にファイルを書くだけでは、Gradleは「何が変わったら作り直す必要があるか」を判断できません。同じファイルを毎回書き直す例から、入力と出力を伝える例へ置き換えます。

03どういう仕組みで解決する?

入力messageの値
generateInfo値をファイルへ書く
出力build-info.properties
矢印はデータの流れ。入力値と出力先をTaskのプロパティで宣言します。

Providerは、後で必要になったときに値を受け取る窓口です。providers.gradleProperty("message") は -Pmessage=... の値を扱い、orElse("hello") で未指定時の値を決めます。すぐget()せず、TaskのPropertyへsetして結びつけます。

04どのファイルを変更する?

変更するのはルートの build.gradle.kts だけです。出力は自動生成されます。入力の値はコマンドで渡すので、設定用の新しいファイルは作りません。

05何を追加・変更する?

まず1回目の置換:次の単純なTaskを貼り付けます。doLastの中だけがファイルを生成する仕事です。

build.gradle.kts第1章の観察コード全体を置換
val looseFile = layout.buildDirectory.file("info/loose.txt")
tasks.register("writeLoose") {
    doLast {
        val file = looseFile.get().asFile
        file.parentFile.mkdirs()
        file.writeText("message=hello\n", Charsets.UTF_8)
    }
}
PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat writeLoose --no-configuration-cache
.\gradlew.bat writeLoose --no-configuration-cache

writeLooseを2回頼みます。build/info/loose.txtが作られますが、出力未宣言なので、2回目もTaskの処理が実行されます。

次に2回目の置換:次の全文へ置き換えます。入力・出力を持つTaskの型GenerateInfoと、その型を使うgenerateInfoを定義します。ここではファイルを一つだけ作るので、この型だけに絞ります。

build.gradle.ktswriteLooseを含む全内容を置換 / importsも含める
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction

abstract class GenerateInfo : DefaultTask() {
    @get:Input
    abstract val message: Property<String>

    @get:OutputFile
    abstract val outputFile: RegularFileProperty

    @TaskAction
    fun generate() {
        val file = outputFile.get().asFile
        file.parentFile.mkdirs()
        file.writeText("message=" + message.get() + "\n", Charsets.UTF_8)
    }
}

val generateInfo = tasks.register<GenerateInfo>("generateInfo") {
    message.set(providers.gradleProperty("message").orElse("hello"))
    outputFile.set(layout.buildDirectory.file("info/build-info.properties"))
}
tasks.register("prepareInfo") {
    dependsOn(generateInfo)
    doLast { println("Info is ready.") }
}
コードこの演習での役割
@Input / @OutputFile比較すべき入力値と生成するファイルをGradleへ伝える
Property / RegularFileProperty入力値と出力先を保持する。Providerも受け取れる
@TaskActionこの型のTaskが実際に行う仕事。先ほどのdoLastと同じく実行時
tasks.registergenerateInfoを遅延登録する。戻り値はTaskProvider
dependsOn(generateInfo)prepareInfoに必要な先行Taskを指定する

06実際に動かして確認する

PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat prepareInfo --no-configuration-cache
.\gradlew.bat prepareInfo --no-configuration-cache
.\gradlew.bat generateInfo -Pmessage=team --no-configuration-cache
Get-Content .\build\info\build-info.properties

prepareInfoがgenerateInfoを実行対象にし、その後にInfo is ready.を表示します。2回目はgenerateInfoがUP-TO-DATE。-Pはプロジェクト用の入力プロパティを渡し、値が変わると再生成されます。ファイルのmessage=teamを確認します。

PowerShell / AdvancedSampleSystemのルート
Remove-Item -LiteralPath '.\build\info\build-info.properties'
.\gradlew.bat generateInfo -Pmessage=team --no-configuration-cache

今生成したファイルだけを削除して再実行します。入力が同じでも、出力が欠けているので再生成されることを確認します。

generateInfoファイルを用意
prepareInfo用意済みと表示
この矢印は「先に必要なTask → 依存するTask」。rootのbuildへはまだつないでいません。

07何が変わった?

入力・出力を宣言したことで、変更のないTaskの仕事を省略できました。入出力を書いただけでBuild Cacheの対象になるわけではありません。このTaskは第5章で、結果を再利用してよい条件を確認してからキャッシュ対応にします。

入力宣言を間違えると?

出力を変える値を入力として宣言し忘れると、古い出力が正しいと判断される恐れがあります。現在時刻や乱数を勝手に出力へ混ぜないでください。この例はUTF-8・改行・messageだけから同じ結果を作り、Task実行中にProjectやbuildスクリプトの状態を参照しません。

次へ進む条件

2回目のUP-TO-DATE、入力変更時の再生成、出力削除後の再生成を確認し、dependsOnの意味を説明できる。

公式資料:Custom Tasks / Lazy Configuration / Incremental Build / Configuration Cache Requirements

03

ADVANCED / 同じビルドを育てる

ライブラリのバージョンを一か所で管理したい

01今回やりたいこと

複数のbuildファイルに散った依存の座標・版を、一つの一覧から参照したい。ここではcommonのCommons LangとappのJUnitを移します。

02なぜ今の状態では困る?

今はライブラリの版を調べるためにappとcommonを開きます。同じライブラリを使うProjectが増えると、更新漏れも起こりやすくなります。

03どういう仕組みで解決する?

Version Catalogは、依存やPluginを別名で参照するためのカタログです。gradle/libs.versions.toml をGradleが標準の場所から読み、libs の参照を用意します。基礎編のinitが作っていたファイルを、ここで初めて使います。

項目入れるもの
[versions]再利用するバージョン名と値
[libraries]ライブラリの別名、module、必要ならversion.ref
[plugins]外部Pluginの別名、ID、版。Gradle組み込みPluginの版は書かない

04どのファイルを変更する?

gradle/libs.versions.toml、app/build.gradle.kts、common/build.gradle.kts を編集します。settingsへのfrom指定は不要です。この標準ファイルをさらに手動importして二重登録しないでください。

05何を追加・変更する?

gradle/libs.versions.toml / 生成済みのコメントだけの内容を置換
[versions]
commons-lang = "3.20.0"
junit = "5.13.4"
versions-plugin = "0.53.0"

[libraries]
commons-lang = { module = "org.apache.commons:commons-lang3", version.ref = "commons-lang" }
junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" }
junit-launcher = { module = "org.junit.platform:junit-platform-launcher" }

[plugins]
versions = { id = "com.github.ben-manes.versions", version.ref = "versions-plugin" }

各buildの dependenciesブロックだけ を次へ置換します。Plugin・Toolchain・repositories・mainClass・test設定はこの章では残します。

app/build.gradle.ktsdependenciesだけ置換
dependencies {
    implementation(project(":common"))
    testImplementation(platform(libs.junit.bom))
    testImplementation(libs.junit.jupiter)
    testRuntimeOnly(libs.junit.launcher)
}
common/build.gradle.ktsdependenciesだけ置換
dependencies {
    implementation(libs.commons.lang)
}
外部Pluginの版も管理するとき

更新候補を調べるVersions Pluginを例にします。カタログの[plugins]へ書くだけでは適用されません。試す場合は次をルートbuildのimportsの後・GenerateInfoクラスの前へ追加し、tasks --allでdependencyUpdatesの登録を確認します。依存の更新調査自体はこの演習では実行不要です。確認後はこのpluginsブロックだけ取り除きます。カタログ内の登録は残して構いません。

build.gradle.kts任意の追加 / 既存Taskは消さない
plugins {
    alias(libs.plugins.versions)
}
PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat tasks --all --no-configuration-cache

外部Pluginの取得・適用後、追加Task名を確認します。Pluginの取得には通信が必要です。

Plugin Portalの0.53.0を演習用に固定します。これはGradle自体の更新とは別です。

06実際に動かして確認する

PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat :app:build --no-configuration-cache
.\gradlew.bat :common:dependencies --configuration runtimeClasspath --no-configuration-cache

ビルド・テストが変わらず成功することと、commonの実行時依存にCommons Lang 3.20.0があることを確認します。保存後、EclipseでもGradle Refreshしてください。

07何が変わった?

参照元はlibsへ変わりましたが、使うライブラリは同じです。commons-lang は libs.commons.lang のような参照になります。JUnitの版の整合は引き続きBOMが担当します。

次へ進む条件

Catalogの変更場所と利用側のlibs参照を結びつけ、ビルド・依存一覧で移行前と同じ結果を確認できる。

公式資料:Version Catalogs

04

ADVANCED / 同じビルドを育てる

複数プロジェクトの共通設定を整理したい

01今回やりたいこと

appとcommonに重複するrepositoriesとToolchainをまとめたい。テストの起動方式とコンパイル文字コードも、Javaプロジェクトの共通ルールとして管理します。

02なぜ今の状態では困る?

同じ設定をコピーし続けると、一方だけJDKの版が変わるなどのずれが生まれます。一方、appのmainClassとcommonのライブラリ依存は、それぞれに残すべきです。

03どういう仕組みで解決する?

Convention Pluginは、チームで使う設定の約束をPluginとしてまとめたものです。適用したProjectだけがルールを受け取ります。今回はJava開発者向けに、両DSLから同じJavaクラスを使う小さなPluginにします。

build-logic共通ルールを定義
guide.java-conventionsapp・commonが明示的に適用
各Project固有のmain・依存は残す
矢印はPluginの提供と適用。appがcommonの設定を変更する関係ではありません。

04どのファイルを変更する?

ファイル役割
settings.gradle.ktsPluginを探す場所としてbuild-logicを登録
build-logic/settings.gradle.kts共通設定を作る別ビルドの名前
build-logic/build.gradle.ktsPluginをビルドし、IDと実装クラスを対応付ける
build-logic/src/main/java/guide/JavaConventionsPlugin.java共通ルールの実装。両DSLで同じJavaコード
app/build.gradle.kts / common/build.gradle.ktsPluginを適用し、各自の固有設定を残す

build-logicはアプリのsubprojectにはしません。includeBuild でPluginを提供する別ビルドとして参加させます。なぜsettingsが別にあるのかは、第6章で同じ構成を使って確かめます。

05何を追加・変更する?

settings.gradle.ktsルートsettingsを全文置換 / pluginManagementは先頭
pluginManagement {
    includeBuild("build-logic")
}
rootProject.name = "AdvancedSampleSystem"
include("app", "common")
build-logic/settings.gradle.ktsフォルダーとファイルを新規作成
rootProject.name = "build-logic"
build-logic/build.gradle.kts新規作成
plugins { id("java-gradle-plugin") }
java { toolchain { languageVersion = JavaLanguageVersion.of(25) } }
gradlePlugin {
    plugins {
        create("javaConventions") {
            id = "guide.java-conventions"
            implementationClass = "guide.JavaConventionsPlugin"
        }
    }
}
build-logic/src/main/java/guide/JavaConventionsPlugin.java / 新規作成・両DSL共通
package guide;

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.api.tasks.testing.Test;
import org.gradle.jvm.toolchain.JavaLanguageVersion;

public class JavaConventionsPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        project.getPluginManager().apply("java");
        project.getRepositories().mavenCentral();
        project.getExtensions().getByType(JavaPluginExtension.class)
            .getToolchain().getLanguageVersion()
            .set(JavaLanguageVersion.of(25));
        project.getTasks().withType(Test.class)
            .configureEach(test -> test.useJUnitPlatform());
        project.getTasks().withType(JavaCompile.class)
            .configureEach(task -> task.getOptions().setEncoding("UTF-8"));
    }
}

java-gradle-plugin がPlugin作成に必要なGradle APIと登録情報の生成を用意します。applyは設定時に呼ばれます。configureEachは、その型のTaskが必要になったときに設定を加えます。ここにTask実行時のファイル生成は書きません。

app/build.gradle.kts全文置換 / mainClass・appの依存はここに残す
plugins {
    application
    id("guide.java-conventions")
}
version = "1.0"
application { mainClass = "guide.Greeting" }

dependencies {
    implementation(project(":common"))
    testImplementation(platform(libs.junit.bom))
    testImplementation(libs.junit.jupiter)
    testRuntimeOnly(libs.junit.launcher)
}
common/build.gradle.kts全文置換 / commonの依存はここに残す
plugins {
    id("java-library")
    id("guide.java-conventions")
}
dependencies {
    implementation(libs.commons.lang)
}

06実際に動かして確認する

PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat :app:build :app:run --no-configuration-cache

共通Pluginが先にビルドされ、app/commonへ適用されます。appのテスト1件成功とHello, Gradle!を確認します。build-logic/build/libs/にはPluginのJARができます。

EclipseはルートAdvancedSampleSystemを開いたまま保存・Gradle Refreshします。build-logic内のファイルをIDEから編集できない場合はOSのエクスプローラーから開けます。Buildshipの版や表示方法により、included buildの見え方は異なります。Wrapperでの成功を先に確認してください。

07何が変わった?

repositories・Toolchain・JUnit Platformの使用・UTF-8は一か所へ移りました。JUnitの依存自体はテストを持つappに残し、Catalogも引き続きapp/commonで使います。build-logicへCatalogを自動継承したとは説明しません。

build-logic自身のToolchainと共通ルールは別

build-logic/buildのJava 25はPluginのJavaコードをコンパイルするための指定です。Plugin内のJava 25は適用先のapp/common用です。同じ数字でも対象が違います。build-logicは独立ビルドなので、ルートのCatalog・repositories等が自動共有されるわけではありません。

subprojects { }でまとめる方式との違い

親から子の設定をまとめて変更すると、各子のファイルだけでは適用ルールが見えにくくなります。ここでは各Projectのpluginsに共通Pluginを明記して、適用範囲を分かりやすくします。precompiled script pluginという別の実装方法もありますが、この演習に重ねて追加する必要はありません。

次へ進む条件

共通設定の変更先と各Project固有の変更先を説明でき、両Projectのビルド・appのテストと起動が成功する。

公式資料:Sharing Build Logic / Task Configuration Avoidance

05

ADVANCED / 同じビルドを育てる

Gradleのビルドを速くしたい

01今回やりたいこと

同じbuildを2回実行すると、なぜ2回目は速いことがあるのか。時間だけでなく「どの仕事を省略・再利用したか」をログから説明できるようにします。

02なぜ今の状態では困る?

何でもキャッシュと呼ぶと、出力が残っていて仕事を省いたのか、消えた出力を復元したのか、設定そのものを再利用したのかが分かりません。

03どういう仕組みで解決する?

仕組み省略・再利用する対象今回見るもの
Up-to-date Check以前の入力と現在の出力を比較し、変更のないTaskの仕事を省略UP-TO-DATE
Incremental Build変更の影響がある仕事を中心に処理。対応Taskは内部の変更部分だけを処理できるJava変更後に必要なTaskが再実行
Build Cache同じ入力・実装等に対応するTask出力をキャッシュから復元FROM-CACHE
Configuration CacheTask実行のための設定結果を保存・再利用Reusing configuration cache.
Gradle Daemon常駐するJVMを再利用し、起動やウォームアップの負担を減らす--statusでプロセス状態

REUSE LAB / 条件を変える概念比較

@CacheableTask対応済み、他の条件・強制再実行なしという想定。実際のTaskログではありません。

SKIPPED はonlyIfなどの条件で実行しない場合の表示で、キャッシュ復元という意味ではありません。NO-SOURCE は処理対象のソースがない場合です。どの省略も同じ理由ではありません。

04どのファイルを変更する?

まずファイル変更なしで比較し、次に common/src/main/java/guide/NameFormatter.java を一時変更します。その後、ルート build.gradle.kts のGenerateInfoへキャッシュ対応を1行加えます。gradle.propertiesの既存値はこの実験では変えず、コマンドのフラグで条件を明示します。

05何を追加・変更する?

  1. まず次のbuildを2回実行し、2回目のUP-TO-DATEを記録します。
  2. NameFormatterの StringUtils.capitalize(name) を StringUtils.capitalize(name.trim()) に変更して保存します。前後の空白を除く実装変更で、既存のgradleという入力ではテスト結果は変わりません。
  3. もう一度同じbuildを実行し、commonのcompileJavaと後続Taskを観察します。確認後は元の式へ戻して保存し、もう一度buildします。
PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat build --no-configuration-cache --no-build-cache
.\gradlew.bat build --no-configuration-cache --no-build-cache

設定再利用・出力キャッシュを無効にして、入力と手元の出力だけによる比較を観察します。ソース変更後・復旧後にもこのコマンドを使います。ルートからのbuildはapp/commonのビルドを選択します。

次に、GenerateInfoの宣言の直前へ @org.gradle.api.tasks.CacheableTask を追加します。以下はクラス宣言の先頭だけです。クラスの中身・imports・Task登録は消さずに残してください。

build.gradle.ktsこの2行は宣言部分の抜粋 / 全文置換しない
@org.gradle.api.tasks.CacheableTask
abstract class GenerateInfo : DefaultTask() {

同じ入力なら同じUTF-8の内容になること、出力に影響するmessageが@Inputであることを確認したうえで付けます。外部サービスの更新など、副作用を起こす仕事を安易にキャッシュ対象へ変えないでください。

06実際に動かして確認する

PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat generateInfo -Pmessage=cache --build-cache --no-configuration-cache
Remove-Item -LiteralPath ".\build\info\build-info.properties"
.\gradlew.bat generateInfo -Pmessage=cache --build-cache --no-configuration-cache

1回目で結果をローカルBuild Cacheへ保存し、生成ファイルだけを削除。2回目にFROM-CACHEとファイルの復元を確認します。既に同じ結果があれば1回目から復元される場合もあります。--build-cacheは対象Taskの出力キャッシュを有効にします。

PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat build generateInfo --configuration-cache --build-cache
.\gradlew.bat build generateInfo --configuration-cache --build-cache

今度は設定結果の再利用も有効にし、同じTask・同じ条件で2回実行します。初回の保存、次回のReusing configuration cache.を探します。設定を再利用していても、Task出力の判定は別に行われます。

PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat --status

利用しているGradle版のDaemonの状態を表示します。キャッシュを消したり、ビルドしたりするコマンドではありません。

毎回フラグを書かずに有効にするなら

ルートgradle.propertiesの既存値を確認し、重複行を作らず編集します。この演習では必須ではありません。

gradle.properties / 該当キーだけ追加または変更
org.gradle.caching=true
org.gradle.configuration-cache=true

Configuration Cache非対応のPluginやTaskがある場合は、問題レポートの該当箇所を直します。単に警告を隠して対応済みにしないでください。BuildshipのImport・同期が、このCLIの設定キャッシュを使うと考えないことも大切です。

07何が変わった?

短いビルドではキャッシュの保存・復元自体の負担もあります。すべてのTaskが対応するわけでも、必ず速くなるわけでもありません。今回は秒数の優劣を結論にせず、ログと出力で各仕組みの動作を確かめます。

Javaソース変更後のログの読み方

commonのコンパイルは必要になりますが、変更の内容やABIによってappの再コンパイルが不要と判断されることもあります。「常に全Taskが再実行」とは決めつけず、実際のTask結果を比べます。インクリメンタルな内部処理も、Taskがその機能を実装している場合に限られます。

次へ進む条件

UP-TO-DATE・FROM-CACHE・設定キャッシュ再利用を別の意味として説明でき、NameFormatterを元に戻しておく。

公式資料:Incremental Build / Build Cache / Configuration Cache / Configuration Cache Requirements / Gradle Daemon

06

ADVANCED / 同じビルドを育てる

別々のGradleビルドを組み合わせたい

01今回やりたいこと

第4章で増えたbuild-logicは、なぜapp/commonと扱いが違うのか。同じ構成を使い、別ビルドを独立して確認したい。

02なぜ今の状態では困る?

同じディレクトリの中に置いてあっても、同じビルドとは限りません。includeとincludeBuildを混ぜると、参照できるProjectや使われる設定の境界を誤解します。

03どういう仕組みで解決する?

BUILD MAP / 構成の比較

一つのsettingsAdvancedSampleSystem
includeapp / common
Project間依存appがproject(":common")を利用
一つのビルドの中へProjectを登録し、利用関係を定義します。
Multi-projectComposite
境界一つのsettingsで複数Project別々のsettingsを持つ複数ビルド
登録include("app", "common")includeBuild("build-logic")
設定の共有親子でも自動継承ではない別ビルドのCatalog・properties等は独立
この演習の用途appがcommonのコードを利用アプリ側が共通Pluginを利用

04どのファイルを変更する?

第4章の settings.gradle.kts と build-logic/settings.gradle.kts を開きます。新しいビルドやフォルダーは追加しません。この章では登録済みのincludeBuildを使い、同じコードを別の入口から実行します。

05何を追加・変更する?

ルートsettingsの先頭が pluginManagement { includeBuild("build-logic") } であることを確認します。Groovyでは第4章の表示どおりの構文を使います。Pluginの解決に使うincluded buildなので、このブロックに置いています。app/commonのincludeはその外に残します。

build-logicのsettingsは名前をbuild-logicにするだけでした。ここにはinclude("app", "common")も、アプリ側のlibsもありません。Pluginを作るビルドにアプリの構成を混ぜないでください。

06実際に動かして確認する

PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat projects --no-configuration-cache
.\gradlew.bat -p build-logic tasks --all --no-configuration-cache
.\gradlew.bat :build-logic:build --no-configuration-cache

1行目はアプリ側の構成を表示します。app/commonのProjectと、Included buildsのbuild-logicを区別します。2行目の-pは、Wrapperを置いたルートに居たまま、対象ビルドの開始ディレクトリをbuild-logicへ切り替えます。3行目はComposite側からincluded buildのbuild Taskを指定します。いずれもアプリのrunではありません。

確かめる箇所読み取れること
projectsのIncluded buildsbuild-logicは別ビルドとして参加している
-p build-logicで表示するTaskPluginのビルド・検証用Taskがある。appのrunとは別
build-logic/build/PluginのJAR等が生成される。app/buildとは別

07何が変わった?

Composite Buildは独立したビルドを組み合わせます。今回はPluginを提供する形でした。一般のライブラリ開発では、別ビルドのgroup:nameに一致する外部依存を、手元のプロジェクトへ置き換える用途もあります。今回はそのために別の演習プロジェクトを増やしません。

buildSrcとは? 今回は作らない

buildSrc/ は、Gradleが特別に認識し、そのビルド向けのPluginやTaskなどを置ける場所です。明示的なincludeBuildなしで扱われます。build-logicは普通の名前の独立ビルドで、どこを取り込むかをincludeBuildで明示し、単独でビルドできます。用途は重なりますが、今回の共通Pluginを両方へ複製する必要はありません。

どちらもビルド用コードの置き場所です。アプリが実行時に使う共通ライブラリを置くcommonとは目的が違います。

次へ進む条件

app/commonとbuild-logicの境界、-pの対象、includeBuildとbuildSrcの役割を区別できる。

公式資料:Composite Builds / Sharing Build Logic

07

ADVANCED / 同じビルドを育てる

Gradleのバージョンを更新したい

01今回やりたいこと

Gradle 9.6.0から 9.8.1 へ更新します。日付は2026年10月11日。更新先は公式のリリースノートで利用可能な版を確認し、この教材の両DSLで実行検証する版に固定します。

02なぜ今の状態では困る?

WrapperのURLを変えて起動できても、使っているPluginや自作のビルド設定が動くとは限りません。変更する要素を分け、更新前後の成功条件をそろえる必要があります。

03どういう仕組みで解決する?

更新するもの今回の扱い
Gradle / Wrapper9.6.0から9.8.1へ。WrapperのTaskで更新
Java25のまま。公式互換表ではGradle起動・Toolchainとも対応範囲
自作Plugin同じソースで再ビルドし、適用先もテスト
第三者Plugin第3章の任意Pluginは取り除いた状態。残す場合は提供元の対応情報と個別テストが必要
依存ライブラリCatalogの版を変更しない

9.8.1のリリースノート、公式の互換表、9.x更新ガイドで変更点を確認します。この演習の成功は、別のプロジェクトのPluginまで互換である保証にはなりません。

04どのファイルを変更する?

元のAdvancedSampleSystemと同じ親フォルダーへ、空の AdvancedSampleSystemUpgrade を作ります。更新試験専用なので、元のフォルダーはそのまま残します。コピーするのは次のソースと設定です。

コピー対象(同じ相対位置)含む内容
準備でコピーしたWrapper・settings・ルートbuild・gradle.properties今の第6章終了時点のもの
gradle/libs.versions.toml現在のCatalog
app / common各buildファイルとsrcだけ
build-logicsettings、buildファイル、srcだけ

各階層のbuild/・.gradle/・Eclipseの.project/.classpath/.settings/は今回も除外します。コピー先のrootProject.nameは変えません。表示名が同じでも、作業場所がUpgradeであることをGet-Locationで確かめます。

05何を追加・変更する?

PowerShell / AdvancedSampleSystemUpgradeへ移動
Set-Location 'C:\自分の練習用フォルダー\AdvancedSampleSystemUpgrade'
Get-Location
.\gradlew.bat --version
.\gradlew.bat build --warning-mode=all --no-configuration-cache

現在位置と9.6.0を確認してから、更新前のビルド・テストを記録します。--warning-mode=allは非推奨を含む警告を個別に表示するオプションです。警告があれば更新ガイドで対象を確かめます。

PowerShell / AdvancedSampleSystemUpgradeのルート
.\gradlew.bat wrapper --gradle-version 9.8.1 --distribution-type bin --no-configuration-cache
.\gradlew.bat wrapper --no-configuration-cache

1回目は現在のGradleで更新先を設定します。2回目は新しいGradleを取得・起動し、その版でWrapper本体と起動スクリプトも生成します。binは実行用の配布物です。新しいGradleの初回取得には通信が必要です。

変更対象は gradle/wrapper/gradle-wrapper.properties、gradle-wrapper.jar、gradlew、gradlew.bat の4ファイルです。propertiesのdistributionUrlに9.8.1が含まれることを確認します。

06実際に動かして確認する

PowerShell / AdvancedSampleSystemUpgradeのルート
.\gradlew.bat --version
.\gradlew.bat clean build --warning-mode=all --no-configuration-cache
.\gradlew.bat build generateInfo --configuration-cache
.\gradlew.bat build generateInfo --configuration-cache

9.8.1の起動、appのテスト1件成功、JAR生成、設定キャッシュ再利用を更新前と比較します。cleanを付けるのは古い出力だけで成功したと判断しないためです。

0 / 6 確認(ページを閉じるとリセット)

07何が変わった?

更新専用コピーで同じテストが成功することを確認できました。問題があれば、そのコピーで調査し、元の9.6.0のフォルダーへ戻れば続きの演習ができます。実際のチームでは、Wrapperの4ファイルを同じ変更として版管理し、関連設定を直した場合も一緒にレビュー・切り戻しできるようにします。

PowerShell / 第8章へ進む前に元の演習コピーへ戻る
Set-Location '..\AdvancedSampleSystem'
Get-Location
.\gradlew.bat --version

第8章は元の演習コピーへ戻り、9.6.0と表示されることを確認します。更新専用コピーは比較用に残します。

更新前にハッシュ値の設定も確認する

gradle-wrapper.propertiesにdistributionSha256Sumを設定しているプロジェクトでは、配布物の版を変えたら、公式配布元が示す新しい版のSHA-256と整合させます。古い値をそのまま使ったり、検証を無効にして通したりせず、チームのWrapper検証手順に従ってください。

次へ進む条件

更新前後のGradle・Java・Plugin・テスト結果を分けて確認でき、9.6.0の元コピーへ戻っている。

公式資料:Gradle Wrapper / Compatibility / Upgrading 9.x

08

ADVANCED / 同じビルドを育てる

CI/CDと成果物の公開につなげたい

01今回やりたいこと

自分のPCで成功したビルドを、別のPCやCIでも再現したい。さらにcommonをMaven形式で渡せるよう、まずローカルの練習用リポジトリへ公開します。

02なぜ今の状態では困る?

IDEの手動設定やPC固有の出力だけに依存していると、クリーンな環境で再現できません。JARを置くだけでは、そのライブラリが必要とする依存情報も伝わりません。

03どういう仕組みで解決する?

ソース取得Wrapper・設定も含む
JDK 25準備起動JavaとToolchain
Wrapperでbuildtestを含む検証
結果を保存JAR・配布物・レポート
矢印はCIの処理の流れ。buildがtestを含むので、続けて同じtestを必ず実行するという意味ではありません。

CIはこの手順を自動で行い、終了コードが非0なら失敗として後続の公開を止めます。maven-publishはJARとPOM等のメタデータを、Maven形式の座標・配置で公開するPluginです。アプリを本番へデプロイする機能とは別です。

04どのファイルを変更する?

作業先は AdvancedSampleSystem(9.6.0) です。common/build.gradle.kts を編集します。出力先はルートの practice-repo/。外部サーバーも認証情報も使いません。CIサービス固有の設定ファイルは、この演習では作りません。

05何を追加・変更する?

common/build.gradle.kts既存のpluginsブロックだけ置換
plugins {
    id("java-library")
    id("guide.java-conventions")
    id("maven-publish")
}

dependenciesはそのまま残し、次をファイル末尾へ追加します。publicationの名前mavenJavaはGradle内の名前、公開する座標は guide.demo:common:1.0 です。

common/build.gradle.ktsdependenciesの外・ファイル末尾へ追加
group = "guide.demo"
version = "1.0"

publishing {
    publications {
        create<MavenPublication>("mavenJava") {
            from(components["java"])
        }
    }
    repositories {
        maven {
            name = "practice"
            url = uri(layout.projectDirectory.dir("../practice-repo"))
        }
    }
}
指定役割
from(components["java"])Javaの成果物と依存情報を出版物へ含める
group / Project名 / versionguide.demo / common / 1.0という座標を決める
publishing.repositories公開先。dependenciesを取得するrepositoriesとは別
../practice-repocommonのディレクトリから見て一つ上の練習用フォルダー

06実際に動かして確認する

PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat clean build --no-daemon --no-configuration-cache
if ($LASTEXITCODE -ne 0) { throw "Build failed; stop before publish." }

まずCIに近い入口としてWrapperでビルドします。PowerShellの$LASTEXITCODEで直前の外部コマンドの終了コードを確認し、失敗時はその場で止めます。--no-daemonは常駐プロセスを再利用しない指定で、単発のDaemonが起動する場合もあります。CIで必須の指定という意味ではありません。

PowerShell / AdvancedSampleSystemのルート
.\gradlew.bat :common:publishMavenJavaPublicationToPracticeRepository
if ($LASTEXITCODE -ne 0) { throw "Publish failed." }
Get-ChildItem .\practice-repo\guide\demo\common\1.0\

commonのmavenJava出版物を、practiceという名前のリポジトリへ公開するTaskです。前のビルドが成功した場合だけ実行します。JAR・POM・module等がこのフォルダーにできることを確認します。publish Task自体がテスト成功を保証するわけではありません。

確認・保存するもの場所
テスト結果app/build/reports/tests/test/index.html
アプリの配布用アーカイブapp/build/distributions/(依存JARと起動スクリプトを含む)
アプリのJARapp/build/libs/app-1.0.jar(単体で全依存を含むわけではない)
公開したcommonpractice-repo/guide/demo/common/1.0/common-1.0.jar
依存情報同じ場所のcommon-1.0.pom。Commons Langの依存を確認

CIでは成功した成果物と、失敗時にも調べられるテストレポート・ログを保存します。保存処理の設定名はサービスごとに異なります。JDK 25、Wrapperの4ファイル、Catalog、build-logicのソースも取得対象にし、必要な依存の取得先へアクセスできるよう準備します。

07何が変わった?

commonをJavaソースのProject間依存だけでなく、Maven形式の成果物として渡せるようになりました。練習用リポジトリはこのPCのフォルダーで、他のPCへ自動共有されたわけではありません。

実際のリポジトリへ公開するとき

公開先、上書き方針、アクセス権をチームで決めます。ユーザー名・パスワード・トークンはbuildファイルやCatalogへ書かず、CIのシークレット管理から環境変数等で渡します。ログにも値を表示しません。この教材は認証情報の入力や外部への公開を要求しません。

CDと再現性はどこまで?

CIは変更ごとのビルド・テストを自動化し、CDは検証済み成果物の配布・リリースへつなげる段階です。本番へのデプロイは今回実施しません。WrapperだけでJDK・OS・依存解決結果まで完全固定されるわけではなく、必要に応じて環境固定や依存ロック等を別途検討します。

次へ進む条件

ビルド失敗時に公開を止める意味を説明でき、ローカルMavenリポジトリのJARとPOMを確認できる。

公式資料:Maven Publish / Gradle Wrapper / Gradle Daemon

公式資料と、この教材の条件

確認日:2026年10月11日。本文の演習は9.6.0で検証し、更新章だけ9.8.1を使います。公式資料のcurrentは参照時9.8.1で、今後更新されます。新しい版だけのAPIを演習へ混ぜないよう、掲載コードの実行結果も確認しています。

実行した検証と、未検証事項(2026年10月11日)

Windows / JDK 25 / Gradle 9.6.0でKotlin・Groovy両方の第1〜8章の主要演習を実行しました。Task登録・設定の表示、入力変更と出力削除、Catalog、共通Plugin、UP-TO-DATE、FROM-CACHE、Configuration Cache再利用、included buildの実行を確認しています。

更新専用コピーではWrapperを9.8.1へ更新し、build・テスト・設定キャッシュ再利用を確認しました。9.6.0の元コピーではローカルMavenリポジトリへのJAR・POMの生成を確認しました。両DSLともappのテストは1件・失敗0件です。第3章の任意Pluginは取得・適用とTask登録を確認し、依存の更新調査Taskは実行していません。

未実施:Eclipse / Buildshipの実機Import・Refresh、macOS/Linuxでのコマンド実行、実際のCIサービス、本番デプロイ、外部リポジトリ公開。ブラウザ内の操作型図は概念例で、Gradleを実行するものではありません。手元の環境やキャッシュ状態によりログ・時間は変わります。

教材本文・図・UIはこのadvanced.htmlに内蔵されています。公式資料の参照と演習に必要な初回取得はオンラインです。基礎編へのリンクを使う場合だけ、index.htmlと同じフォルダーに置いてください。

印刷前に補足と構成比較を展開し、印刷後に元へ戻します。コードは選択中のDSLで印刷します。