Gradle 9.9を9.8.2と比べて検証|--testsでArchUnitの規則を選べるようになり、wrapper更新で設定が残る

Gradle 9.9.0が11月6日に正式公開。主な変更点を9.8.2と並べて動かした

ビルドツールGradleの開発元Gradle, Inc.は2026年11月6日、Gradle 9.9.0を正式に公開しました。10月30日に公開されたRC1(リリース候補の9.9.0-rc-1)から、RC2を挟まずに1週間で正式版になり、リリースノートに書かれた変更点はRC1と同じです。Gradleは、AndroidアプリやKotlin、Javaのプロジェクトでビルドとテストを動かす道具で、Android Studioで作ったプロジェクトもGradleで動いています。

9.9の変更で読者の作業に関係するのは、テストを絞り込む --tests オプションの挙動、Gradleを更新するときのwrapperの設定、ビルド設定ファイル(Kotlin DSL)の再コンパイル、ビルド設定で使うProviderの新しい作り方の4点です。リリースノートを読むだけでは、自分のプロジェクトで何が変わるかが分かりにくいので、同じサンプルプロジェクトを一つ前の9.8.2と9.9 RC1の両方で動かし、出力と設定ファイルの違いを確かめました。正式版の公開後に、9.9.0でも同じコマンドを再実行し、結果がRC1と一致することを確かめています。

この記事では、確かめた結果を変更点ごとに示し、最後に更新前に確認する3点をまとめます。

9.9で変わる4点(同じサンプルを両版で動かした結果つき)

テストの絞り込みが指定どおりになり、wrapperの設定は再生成後も残る

同じサンプルをGradle 9.8.2と9.9.0-rc-1で実行し、正式版の9.9.0で再実行した結果。詳しい出力は各節に載せています。

--tests の絞り込み

ArchUnitの規則も1つずつ選べる

9.8.2では規則が全部走った指定で、9.9は選んだ規則だけが走る。

wrapper の更新

書き換えた設定が消えない

networkTimeoutなどを変えていても、再生成後にそのまま残る。

Kotlin DSL の再コンパイル

変わった部分だけをコンパイル

Gradleの計測では大規模ビルドで65%速い。スクリプト4本のサンプルでは9.8.2でも9.9でも6〜10秒。

ProviderFactory

present / absent / presentIfNotNull が書ける

値が決まっているProviderを、Callableなしで作れる。

検証に使ったのは、buildSrc付きの3モジュール構成とArchUnitのテスト

比較に使ったサンプルは、build.gradle.kts(Kotlin DSL)で書いた3つのサブプロジェクト(app、lib、core)と、バージョン番号を定数で持つ buildSrc からなる小さなJavaプロジェクトです。appには、通常のJUnit 5のテストを1つと、コードの依存関係のルールをテストとして書くライブラリ ArchUnit の規則を3つ置きました。ArchUnitの規則はメソッドではなく @ArchTest を付けたフィールドに書くので、今回の --tests の変更をそのまま確かめられます。

検証の構成(2026年11月1日に実行、11月8日に9.9.0で再実行)

サンプル

Kotlin DSL、buildSrc(定数オブジェクト)、サブプロジェクト app / lib / core。JUnit Jupiter 5.12.2、ArchUnit JUnit5 1.4.1

テスト

SomeUnrelatedTest(通常のテスト1件)、ArchRulesTest(規則 firstRule、secondRule と、ArchTests.in でまとめた NestedRules.nestedRule)

環境

macOS 27.0.1、JDK 21.0.8、Gradle 9.8.2 と 9.9.0-rc-1(wrapperで切り替え)、--no-daemon。正式版の9.9.0は同じ環境で11月8日に再実行

正式版9.9.0での再実行では、--tests の指定ごとに走ったテストの件数、wrapper再生成後の設定ファイル、app/srcから実行したときのエラー、ProviderFactoryの出力のすべてがRC1と同じでした。本文の比較カードとコンソール出力はRC1で取ったものを載せ、計測表には9.9.0の列を加えています。

--testsが、ArchUnitのようにメソッドで書かれていないテストにも効くようになった

./gradlew test --tests "..." は、実行するテストをクラス名やメソッド名で絞り込むオプションです。9.8.2までは、クラスがパターンに合う限り、フィールドに書かれたArchUnitの規則は指定に関係なく全部走っていました。9.9では、規則も「クラス名.規則名」で1つずつ選べます。

手元の結果を、指定ごとに並べます。実行後のコンソールの PASSED 行と app/build/test-results/test/ のXMLで数えたテスト件数です。

下の4枚のカードで見ること

9.8.2はクラスが合えば規則3つが全部走り、9.9は指定に一致した規則だけが走る(一致しなければ0件)。

1. --tests "*greetsByName"(クラスはワイルドカード、メソッド名を指定)

Gradle 9.8.2

4件(greetsByName+規則3つ)

クラスのパターンに合うArchRulesTestの規則が、名前に関係なく全部走った。

Gradle 9.9.0-rc-1

1件(greetsByName だけ)

規則は名前が合わないので走らない。

2. --tests "ArchRulesTest.firstRule"(規則を1つ指定)

Gradle 9.8.2

3件(規則が全部)

firstRule だけを指定しても、secondRule と nestedRule も走った。

Gradle 9.9.0-rc-1

1件(firstRule だけ)

指定した規則だけが走る。

3. --tests "ArchRulesTest.NestedRules > nestedRule"(ArchTests.in でまとめた規則)

Gradle 9.8.2

3件(規則が全部)

指定の形に関係なく全部走った。

Gradle 9.9.0-rc-1

1件(nestedRule だけ)

まとめ先のクラス名(NestedRules)と規則名で一致する。

4. --tests "ArchRulesTest.nested"(まとめた規則を持つフィールド名で指定)

Gradle 9.8.2

3件(規則が全部)

クラスが合うので、フィールド名の指定でも規則が全部走った。

Gradle 9.9.0-rc-1

0件(No tests found で失敗)

フィールド名は一致の対象に入らない。報告される名前で指定する。

件数は、各実行のコンソールに出た PASSED 行と app/build/test-results/test/ のXMLで数えた。クラス名だけの指定(--tests "SomeUnrelatedTest")は両版とも1件で、ArchRulesTestはクラスごと除外される。

指定2の実際のコンソール出力(抜粋)です。9.8.2では指定していない規則まで PASSED と出ています。

$ ./gradlew test --tests "ArchRulesTest.firstRule"

# Gradle 9.8.2
ArchRulesTest > firstRule PASSED
ArchRulesTest > secondRule PASSED
ArchRulesTest > NestedRules > NestedRules > nestedRule PASSED
BUILD SUCCESSFUL in 3s

# Gradle 9.9.0-rc-1
ArchRulesTest > firstRule PASSED
BUILD SUCCESSFUL in 3s

この変更で、--tests を使うCIの設定は、走るテストが減ることがあります。これまで「指定していないのに毎回走っていた」ArchUnitの規則が、9.9からは指定したものだけになるからです。規則を毎回走らせたい場合は、クラス名(--tests "ArchRulesTest")を指定に加えます。Gradleのアップグレードガイドは、規則名の部分はテストエンジンが報告する名前で、エンジン側の更新で変わることがあるため、可能なら囲むクラス名で指定するよう勧めています。Kotestのように実行中にテストを登録するものは、これまでどおりクラス単位でしか選べません。

wrapperを再生成しても、networkTimeoutなどの設定が消えなくなった

Gradleの更新は ./gradlew wrapper --gradle-version 9.9.0 のように wrapper タスクで行い、このとき gradle/wrapper/gradle-wrapper.properties が書き直されます。9.8.2までは、ここに書いたネットワークのタイムアウトやリトライ回数などの設定が、再生成のたびに既定値へ戻っていました。9.9では残ります。

手元では、タイムアウトを30秒、リトライを5回、リトライの間隔を2秒に変え、URLの検証を切った状態で、同じ版を指定して wrapper タスクを実行しました。

gradle-wrapper.properties の再生成前後

書き換えた4項目(networkTimeout、retries、retryBackOffMs、validateDistributionUrl)が、再生成後にどうなったか。

Gradle 9.8.2

4項目とも既定値へ戻った

networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true

Gradle 9.9.0-rc-1

4項目ともそのまま残った

networkTimeout=30000
retries=5
retryBackOffMs=2000
validateDistributionUrl=false

保持されるのは distributionBase、distributionPath、zipStoreBase、zipStorePath、networkTimeout、validateDistributionUrl、retries、retryBackOffMs。distributionUrl は指定した版のURLに作り直される。

2つ注意点があります。distributionUrl は毎回作り直されるので、社内のミラーなど独自のURLを使っている場合は --gradle-distribution-url を付けて指定します。また、アップグレードガイドによると、既存の値が不正なとき(数値であるべき networkTimeout に文字が入っているなど)は、9.8.2までは黙って上書きされていましたが、9.9では wrapper タスクが失敗します。

Kotlin DSLのスクリプトは差分コンパイルになった。スクリプト4本のサンプルではどの版も6〜10秒

build.gradle.kts のようなKotlin DSLのスクリプトは、buildSrc や included build の中身が変わると、コンパイルし直されます。9.9からは、このコンパイルがKotlin Build Tools APIを通して行われ、変わった部分だけをコンパイルする差分コンパイルが効きます。Gradleは、大規模なマルチプロジェクトの性能試験で再コンパイルが65%速くなったとしています。スクリプトをめったに再コンパイルしないビルドでは変化はありません。

下の表で見ること

スクリプト4本の小さなサンプルでは、buildSrcを変えたあとの再コンパイルは9.8.2でも9.9でも6〜10秒だった。65%は大規模ビルドでのGradleの数値。

./gradlew help の実時間 Gradle 9.8.2 Gradle 9.9.0-rc-1 Gradle 9.9.0(11月8日に再実行)
変更なし 2.6秒 2.8秒 2.9秒
buildSrc に定数を追加した直後(1回目) 8.9秒 8.8秒 9.3秒
さらに定数を追加した直後(2回目) 8.0秒 6.5秒 7.5秒

各1回の計測(/usr/bin/time の real)で、版の間の差は比べていない。--no-daemon で実行。buildSrc変更後の1回目はKotlinコンパイラのデーモン起動を含み、2回目は同じデーモンを使い回した可能性がある。サンプルはプロジェクトのビルドスクリプト4本(ルート+3サブプロジェクト)とbuildSrcのKotlinファイル1本。

組み込みのKotlinも9.8.2の2.4.10から2.4.21へ上がっています。--info のログでは、両版ともbuildSrcのコンパイルにKotlin Build Tools API経由の差分コンパイル(INCREMENTAL_COMPILER)が使われていました。9.9で新しいのは、スクリプト本体のコンパイルがこの経路に乗った点だと、リリースノートは説明しています。効果を見るには、スクリプトの本数が多いプロジェクトで、buildSrcの変更後のビルド時間を更新前後で比べるのが確実です。

ProviderFactoryに、値が決まっている場合の present / absent / presentIfNotNull が加わった

Providerは、Gradleのビルド設定で「値をあとで評価する入れ物」として使う型です。これまで、すでに決まっている値をProviderにするには providers.provider { value } のようにCallableで包む書き方が必要でした。9.9では、present(value)、absent()、presentIfNotNull(nullableValue) の3つがProviderFactoryに加わり、意図をそのまま書けます(いずれも試験的な@Incubating API)。

// build.gradle.kts
val fixed: Provider<String> = providers.present("fixed-value")
val nothing: Provider<String> = providers.absent()
val maybe: Provider<String> = providers.presentIfNotNull(System.getenv("SAMPLE_OPTIONAL"))

tasks.register("showProviders") {
    doLast {
        println("fixed.isPresent=" + fixed.isPresent + " value=" + fixed.get())
        println("absent.isPresent=" + nothing.isPresent)
        println("presentIfNotNull.isPresent=" + maybe.isPresent + " value=" + maybe.getOrElse("(none)"))
    }
}
# Gradle 9.8.2(出力の抜粋)
e: build.gradle.kts:2:41: Unresolved reference 'present'.
e: build.gradle.kts:3:43: Unresolved reference 'absent'.
e: build.gradle.kts:4:41: Unresolved reference 'presentIfNotNull'.

# Gradle 9.9.0-rc-1
$ ./gradlew showProviders
fixed.isPresent=true value=fixed-value
absent.isPresent=false
presentIfNotNull.isPresent=false value=(none)

$ SAMPLE_OPTIONAL=hello ./gradlew showProviders
presentIfNotNull.isPresent=true value=hello

9.8.2ではスクリプトのコンパイルで3つとも Unresolved reference になり、9.9では環境変数の有無に応じて presentIfNotNull が present と absent を切り替えました。あわせて、アップグレードガイドによると、必須の入力ファイルのプロパティに absent なProviderをリストで渡すと、9.9では検証で失敗します。省略してよい入力は optional() か @Optional を付けます。

更新前に確認する3点。--testsの指定、実行するディレクトリ、AGPの対応範囲

ここまでの変更のうち、既存のプロジェクトで手を入れる可能性があるのは次の3点です。

9.9へ上げる前の確認

1. --tests と CI の設定

フィールドに書いたArchUnitの規則が走らなくなる指定を探す

ArchUnit や Spek を使っていて、--tests、filter ブロック、include/exclude で絞っているなら、規則が走るクラスを指定に加える。

2. 実行するディレクトリ

ビルド内の、プロジェクトでないディレクトリからの実行はエラーになる

手元では app/src から gradlew help を実行すると、9.8.2は成功し、9.9は「Project directory ... is not part of the build defined by settings file」で失敗した。スクリプトの cd 先を確かめる。

3. Android プロジェクト

AGP が対応する Gradle の範囲を先に見る

AGP 9.5.0 の最小は Gradle 9.6.0。9.9.0 への更新は、AGP の対応表と Android Studio の対応を確かめてから、本番のプロジェクトとは別の作業ツリーで試す。

更新そのものは2段階のコマンドです。1回目で wrapper の設定を書き換え、2回目で新しい版の wrapper がスクリプト類を更新します。

./gradlew :wrapper --gradle-version=9.9.0 && ./gradlew :wrapper

このほか、リリースノートには、Problems API(ビルドの問題を整理して報告する仕組み)の問題IDが「Compilation > Java」のような定義済みグループへ移った変更があります。Build Scan やTooling APIで問題IDを文字列で照合しているツールは更新が必要です。Ant統合、Kotlin DSLの zipTo / unzipTo、古い形式のロックファイルなどが非推奨になり、Gradle 10で削除される予定です。

9.9.0へ上げる前に、--testsの指定とwrapperの差分を自分のプロジェクトで確かめる

今回の変更で実際に手を入れる可能性があるのは、--tests でArchUnitの規則が「走らなくなる」設定と、ビルドの中のプロジェクトでないディレクトリ(app/src など)から gradlew を呼んでいるスクリプトの2つです。どちらも、別の作業ツリーで9.9.0の wrapper に切り替えてCIと同じコマンドを一度流せば分かります。

今回の変更で便利だと思うのは、wrapper の設定が消えなくなった点です。設定を保持するのは新しい版の wrapper タスクなので、9.8以前から9.9.0へ上げる最初の1回は、今入っている版のタスクが動くため、仕組み上、従来どおり既定値に戻ります。9.9に乗ったあとの更新からは、distributionUrl の1行の差分を確かめるだけで済みます。まず wrapper だけ9.9.0へ上げて設定を一度書き戻し、次の更新でこの1行の差分を確かめるところから始めるのがよさそうです。