Gradleとは何か:Javaプロジェクトのビルド自動化ツールの基礎

Gradleの役割とできること

Gradleは、JavaなどのJVMプロジェクトにおけるビルド処理を自動化するためのオープンソースツールです。ソースコードのコンパイル、テスト実行、外部ライブラリの取得と依存関係の解決、JARなどへのパッケージング、アプリケーションの実行、成果物の公開までを一貫して自動化できます。Gradle自体はJavaコンパイラではなく、これらの処理をタスクとして構成・実行する役割を担います。

Mavenとの違いとビルドスクリプト

MavenもJavaで広く使われるビルドツールですが、GradleはGroovyまたはKotlinを使ったDSLで柔軟にビルド処理を記述できる点が大きな違いです。ビルド設定はbuild.gradle(Groovy DSL)またはbuild.gradle.kts(Kotlin DSL)に記述し、プロジェクト全体の構成はsettings.gradleまたはsettings.gradle.ktsに書きます。「GradleはMavenの後継」という単純な関係ではなく、両方とも現在利用されているツールです。

Gradle Wrapperの重要性

Javaプロジェクトでは、Gradle本体を手動インストールしてgradleコマンドを使うより、Gradle Wrapper(gradlew / gradlew.bat)を使うことが公式推奨です。Wrapperはプロジェクトごとに使用するGradleバージョンを固定し、必要なバージョンのGradleを自動的に取得・実行します。gradle-wrapper.propertiesにバージョンが記載されており、開発者全員やCI環境で同じGradleバージョンを使用しやすくなります。

ビルド設定を記述するbuild.gradleやbuild.gradle.ktsはプロジェクトのルートディレクトリに配置され、タスクの定義や依存関係の宣言など、ビルド処理の中心となる内容を記述します。

要点:Gradleはビルド自動化ツールであり、Java開発ではWrapperを使ったバージョン固定が基本。ビルド設定はGroovyまたはKotlin DSLで記述する。

Gradleで発生する代表的なエラーとその対処法

依存関係が解決できないエラー

「Could not resolve …」エラーは、依存関係の解決に失敗したときに発生します。まずrepositoriesブロックにmavenCentral()などのリポジトリ宣言が正しくあるか、依存するアーティファクトのgroup・artifact・versionにタイポがないかを確認しましょう。推移的依存関係の競合が疑われる場合は、dependencyInsightタスクで調査できます。

./gradlew :app:dependencyInsight --dependency junit

このコマンドで、特定の依存関係がどのプロジェクトからどう解決されているかを追跡できます。

メモリ不足(OutOfMemory)エラー

Gradleデーモンのデフォルト最大ヒープサイズは-Xmx512mです。大規模プロジェクトでは不足することがあります。gradle.propertiesに以下のように設定してメモリを増やします。

org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

なお、org.gradle.jvmargsはGradleデーモン(ビルドVM)の設定であり、コマンドライン表示を行うクライアントVM(JAVA_OPTS)や、テストを実行するフォークJVMとは別物です。testタスクのフォークVMもデフォルトで-Xmx512mなので、テストでメモリ不足になる場合はtestタスク側の設定も確認する必要があります。

Javaバージョン不一致エラー

「Unsupported class file major version」エラーは、Gradleを実行するJDKバージョンが、プロジェクトのコンパイル対象Javaバージョンと一致しない場合に発生します。Gradleの実行にはJDK 17以上が必要で、JavaのバージョンとGradleのバージョンは別物です。Gradleの互換性表を確認し、WrapperのGradleバージョンとJDKバージョンの組み合わせを整合させましょう。Java Toolchainsを使えば、Gradleを実行するJDKとプロジェクトのコンパイル・テストに使うJava環境を分離できます。

要点:エラー発生時は依存関係の宣言、メモリ設定、Javaバージョンの3点を確認するのが基本。–stacktraceや–infoオプションで詳細ログを取得できる。

Gradleのテスト実行方法とJUnit連携の基本

テストタスクの基本と実行コマンド

javaプラグインを適用すると、testタスク(単体テスト実行)とcheckタスク(すべての検証タスクをまとめたライフサイクルタスク)が提供されます。デフォルトのテストソースディレクトリはsrc/test/javaで、基本的な実行コマンドは以下のとおりです。

  • ./gradlew test:すべてのサブプロジェクトのテストを実行(ルートから実行した場合)
  • ./gradlew :app:test:特定のサブプロジェクトのみテスト実行
  • ./gradlew build:コンパイルとテストを含むビルド全体を実行

テスト結果はbuild/reports/tests/test/index.htmlにHTMLレポートとして、build/test-results/test/にJUnit XMLフォーマットで出力されます。

テストの絞り込み(–testsオプション)

コマンドラインで–testsオプションを使うと、実行するテストを絞り込めます。

./gradlew test --tests "com.example.MyTest"
./gradlew test --tests "com.example.MyTest.someMethod"
./gradlew test --tests "com.example.*Test"

複数指定も可能で、パターンにマッチするテストが見つからない場合は例外がスローされます。これは誤って0件実行を成功扱いしないための仕組みです。ビルドスクリプト側ではtestタスクのfilterブロックでincludeTestsMatchingを指定できます。

JUnit 5(Jupiter)を使うための設定

GradleのtestタスクはデフォルトではJUnit Platformを有効化していないため、JUnit 5を使うには明示的な設定が必要です。

test {
    useJUnitPlatform()
}
dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.x.x'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.x.x'
}

useJUnitPlatform()を呼ぶことでJUnit 5のテストが実行されます。また、includeTagsやexcludeTagsを使うとテストをタグでグループ化して実行対象を制御できます(出典:Gradle User Manual「Building Java & JVM projects」)。

要点:JUnit 5を利用する場合はuseJUnitPlatform()の設定が必須。–testsオプションでテストを絞り込み、HTML/XMLレポートが自動生成される。

Gradleのサブプロジェクト構成で知っておくべき重要ポイント

サブプロジェクトの宣言方法

マルチプロジェクトビルドは1つのルートプロジェクトと1つ以上のサブプロジェクトで構成され、すべてsettings.gradleまたはsettings.gradle.ktsでinclude()を使って宣言します。

// Kotlin DSL
rootProject.name = "my-project"
include("app", "core", "util")

include()に渡すプロジェクトパスはデフォルトで相対ファイルシステムパスに対応し、include(“services:hotels:api”)のように指定すると、services、services:hotels、services:hotels:apiの3つのプロジェクトが自動的に作成されます。

Gradle 9.0からの重要な変更点

Gradle 9.0以降、includeで宣言したサブプロジェクトのディレクトリが存在し、書き込み可能であることが必須になりました。従来はディレクトリが欠落していても静かに許可されていましたが、現在はビルドが失敗します。設定時にディレクトリを自動作成するには、以下のようにプロジェクトディレクトリを明示的に作成します。

include("project-without-directory")
project(":project-without-directory").projectDir.mkdirs()

Gradle 9系にアップグレードした際に発生するエラーの原因として注意が必要です。

サブプロジェクト間の依存関係とテスト実行

サブプロジェクト間の依存はproject()で宣言します。

dependencies {
    implementation project(':shared')
    implementation project(':api')
}

プロジェクト依存は実行順序に影響し、依存先プロジェクトのクラス出力とその依存関係がクラスパスに追加されます。テスト実行では、ルートからパスなしで./gradlew testを実行すると階層下の全サブプロジェクトのtestタスクが実行される一方、tasks.testを参照したタスク依存の宣言は完全パスのみを指すため、サブプロジェクトのtestタスクには自動的に波及しません。この違いは誤解されやすいポイントです。

たとえば、ルートプロジェクトのbuild.gradle.ktsでtasks.test { dependsOn(subprojects.map { it.tasks.test }) }のように宣言しても、サブプロジェクトのtestタスクには依存が波及しません。各サブプロジェクトのtestタスクに明示的に依存関係を設定する必要があります。

要点:サブプロジェクトはsettings.gradleでinclude宣言し、Gradle 9.0以降はディレクトリ存在が必須。ルートからのtest実行は全サブプロジェクトに波及する。

ビルド効率を高めるGradle操作の実践テクニック

Gradleデーモンの管理とメモリ最適化

Gradleデーモンはビルドの高速化に寄与しますが、不安定になった場合は./gradlew –stopで浮遊デーモンを停止できます。原因切り分けのためにデーモンを無効化して実行する場合は、以下のコマンドを使います。

./gradlew build --no-daemon

デーモンのメモリ設定はgradle.propertiesのorg.gradle.jvmargsで行います。-Xmxを指定するとデフォルトの他のJVM引数がクリアされる既知の問題があるため、必要なJVM引数は明示的にすべて指定するのが安全です。

キャッシュクリアと詳細ログによる問題切り分け

増分ビルドやキャッシュ起因の問題が疑われる場合は、cleanタスクで全ビルド出力を削除して再実行します。

./gradlew clean build

さらにプロジェクトキャッシュである.gradleディレクトリとbuildディレクトリを削除して再ビルドすると、キャッシュ由来の問題をリセットできます。詳細なログが必要な場合は、–stacktraceや–infoオプションを付けて実行します。

./gradlew test --stacktrace --info

buildSrcによる共有ビルドロジックの活用

サブプロジェクトで共通するビルドロジック(Javaバージョン、使用ライブラリなど)は、buildSrcディレクトリに置いて共有できます。buildSrcの内容は自動的にコンパイルされ、全ビルドスクリプトのクラスパスに含まれます。

  • buildSrcはマルチプロジェクトビルドにつき1つだけ
  • ルートディレクトリに配置する必要がある
  • allprojectsやsubprojectsによる共通設定注入より、buildSrcやconvention pluginの利用が公式推奨

allprojectsやsubprojectsによる設定注入はプロジェクトを結合させ、並列実行やconfiguration on demandと相性が悪いため避けるべきです。

要点:デーモンのメモリ設定はorg.gradle.jvmargsで行い、トラブル時はcleanとログオプションで切り分ける。共通設定はbuildSrcで共有するのが推奨。