Gradle with Kotlin

Gradle with Kotlin DSL: 詳細技術解説

目次

  1. はじめに
  2. Gradleの基礎概念
  3. Kotlin DSLの紹介
  4. アーキテクチャ概要
  5. 設定の具体例
  6. 依存関係管理
  7. マルチプロジェクト構成
  8. パフォーマンス最適化
  9. ベストプラクティス
  10. トラブルシューティング

1. はじめに

1.1 Gradleとは

Gradle は、Apache 2 ライセンスの下で公開されているビルドオートメーションツールです。元々は Java プロジェクト向けに開発されましたが、現在では Kotlin、Groovy、C/C++、Python など多くの言語をサポートしています。

Gradle の最大の特徴は、その柔軟性と拡張性にあります。従来の XML ベースの設定ツール(Maven など)と異なり、Gradle はプログラミング言語ベースの DSL(ドメイン固有言語)を使用することで、複雑なビルドロジックを簡潔かつ理解しやすく記述できます。

1.2 Kotlin DSL が選ばれる理由

Gradle は長年、設定言語として Groovy を使用してきました。しかし、以下の理由から Kotlin DSL が推奨されるようになりました:

  • 型安全性: Kotlin の静的型システムにより、ビルド設定での型チェックが可能
  • IDE サポート: コード補完やリファクタリング機能が充実
  • コンパイル時チェック: 構文エラーをより早い段階で検出
  • 保守性: クリーンで読みやすいコード

1.3 ドキュメント構成

本ドキュメントでは、Gradle and Kotlin DSL の以下の側面を詳しく解説します:

  • Gradle のコアコンセプト(タスク、プロジェクト、プラグイン)
  • Kotlin DSL の文法と使用方法
  • アーキテクチャとビルドライフサイクル
  • 実践的な設定例
  • 大規模プロジェクトでの活用方法

2. Gradleの基礎概念

2.1 プロジェクト(Project)

Gradle における最小の作業単位はプロジェクトです。各プロジェクトは build.gradle.kts ファイルに設定を持ちます。

// build.gradle.kts (プロジェクト設定ファイル)
plugins {
    kotlin("jvm") version "1.9.21"
    id("application")
}

group = "com.example"
version = "1.0.0"

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

repositories {
    mavenCentral()
    maven {
        url = uri("https://repo.example.com/maven")
    }
}

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.21")
    testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
}

application {
    mainClass.set("com.example.AppKt")
}

各プロジェクトには以下の属性があります:

属性説明
nameプロジェクト名
groupグループ識別子(パッケージ名のような役割)
versionバージョン番号
descriptionプロジェクトの説明

2.2 タスク(Task)

タスクは Gradle の実行単位です。コンパイル、テスト実行、パッケージング、デプロイなど、ビルドプロセスのあらゆるステップがタスクとして定義されます。

// タスクの定義例
tasks.register("customGreeting") {
    description = "Simple greeting task"
    group = "custom"
    
    doLast {
        println("Hello from Gradle!")
    }
}

// 複数の依存関係を持つタスク
tasks.register("deployment") {
    description = "Deploy the application"
    group = "deploy"
    dependsOn(tasks.build, tasks.test)
    
    doLast {
        println("Deploying application...")
        // デプロイロジック
    }
}

// 既存タスクの拡張
tasks.test {
    useJUnitPlatform()
    testLogging {
        events("passed", "skipped", "failed")
    }
}

2.3 プラグイン(Plugin)

プラグインは、Gradle 機能を拡張するコンポーネントです。言語サポート、テストフレームワークサポート、IDE 統合、デプロイメント機能など、様々な機能を提供します。

plugins {
    // Kotlin JVM プラグイン
    kotlin("jvm") version "1.9.21"
    
    // Java プラグイン
    id("java")
    
    // テストカバレッジプラグイン
    id("jacoco")
    
    // Spring Boot プラグイン
    id("org.springframework.boot") version "3.1.5"
    
    // カスタムプラグイン
    id("com.example.custom-plugin") version "1.0"
}

2.4 プロパティとメタデータ

// プロジェクトプロパティの定義
val jvmVersion: String by project
val isCI: String by project

java {
    sourceCompatibility = JavaVersion.VERSION_17
}

// カスタムプロパティの定義
extra["isReleaseVersion"] = !version.toString().endsWith("SNAPSHOT")
val isRelease: Boolean by extra

// 環境変数の読み込み
val buildNumber: String = System.getenv("BUILD_NUMBER") ?: "LOCAL"

// settings.gradle.kts から渡されるプロパティ
println("Project name: ${project.name}")
println("Root project: ${rootProject.name}")

3. Kotlin DSL の紹介

3.1 Kotlin DSL の構文基礎

Kotlin DSL は Kotlin のラムダ式と拡張関数を活用した、エレガントな DSL です。

// 基本的なブロック構造
dependencies {
    // ラムダの中で依存関係を定義
    implementation("org.jetbrains.kotlin:kotlin-stdlib")
    testImplementation("junit:junit:4.13.2")
}

// 条件付き依存関係
dependencies {
    if (project.property("includeTestLibs").toString().toBoolean()) {
        testImplementation("org.mockito:mockito-core:5.2.0")
        testImplementation("org.assertj:assertj-core:3.24.1")
    }
}

// ループを使用した動的設定
val testFrameworks = listOf("junit", "testng", "spock")
dependencies {
    testFrameworks.forEach { framework ->
        if (framework == "junit") {
            testImplementation("junit:junit:4.13.2")
        }
    }
}

3.2 型安全性とコード補完

Kotlin DSL の最大の利点は IDE によるコード補完です。

// IDE が自動補完を提供
tasks.named<Test>("test") {
    useJUnitPlatform()
    testLogging {
        events("passed", "skipped", "failed")  // イベント名の補完
        exceptionFormat = "full"                 // プロパティ名の補完
        showStandardStreams = true               // 値の型チェック
    }
}

// プラグインの設定も型安全
repositories {
    mavenCentral()      // 定数から選択可能
    google()            // カスタムリポジトリの補完
}

3.3 拡張関数とカスタム DSL

// カスタム拡張関数の定義
fun Project.isCI(): Boolean = System.getenv("CI") != null

fun RepositoryHandler.customRepos() {
    mavenCentral()
    maven {
        url = uri("https://repo.example.com/maven")
        credentials {
            username = System.getenv("REPO_USER")
            password = System.getenv("REPO_PASSWORD")
        }
    }
}

// 使用例
repositories {
    customRepos()
}

// 条件付き設定
if (isCI()) {
    println("Running in CI environment")
}

3.4 Groovy DSL との互換性

多くのプロジェクトは Groovy DSL (build.gradle) を使用していますが、Kotlin DSL (build.gradle.kts) への移行は段階的に行えます。

// Groovy DSL (build.gradle)
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.5'
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

同等の Kotlin DSL:

// Kotlin DSL (build.gradle.kts)
plugins {
    id("java")
    id("org.springframework.boot") version "3.1.5"
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

4. Gradle アーキテクチャ概要

4.1 ビルドライフサイクル

Gradle のビルドプロセスは 3 つのフェーズで構成されます:

フェーズ 1: 初期化フェーズ (Initialization)

この段階で、Gradle はビルドに参加するプロジェクトを特定します。

// settings.gradle.kts
rootProject.name = "my-application"

include(
    "app",
    "core",
    "utils",
    "api"
)

// 依存関係を持つモジュールの定義
project(":api").projectDir = file("modules/api")

フェーズ 2: 設定フェーズ (Configuration)

このフェーズで、すべての build.gradle.kts ファイルが読み込まれ、タスクグラフが構築されます。

// build.gradle.kts

// この部分は設定フェーズで実行される
println("Configuring project ${project.name}")

// タスク定義も設定フェーズで評価される
tasks.register("myTask") {
    println("This runs during configuration")
    
    doLast {
        println("This runs during execution")
    }
}

フェーズ 3: 実行フェーズ (Execution)

要求されたタスクが実行されます。

$ gradle build test

// 出力例:
// Configuring project app
// Configuring project core
// > Task :app:build
// > Task :app:test
// > Task :core:build
// > Task :core:test

4.2 タスクグラフ

タスク間の依存関係は DAG (有向非環グラフ) として表現されます。

// 依存関係の定義
tasks.register("compile") {
    description = "Compile sources"
    doLast {
        println("Compiling...")
    }
}

tasks.register("test") {
    description = "Run tests"
    dependsOn(tasks.named("compile"))
    doLast {
        println("Testing...")
    }
}

tasks.register("build") {
    description = "Build project"
    dependsOn(tasks.named("test"))
    doLast {
        println("Building...")
    }
}

// 実行順序: compile -> test -> build

ビジュアライゼーション:

    compile
      |
      v
     test
      |
      v
    build

4.3 キャッシング機構

Gradle はビルド結果をキャッシュして、パフォーマンスを向上させます。

// タスクの出力キャッシングを有効化
tasks.register<JavaCompile>("compileKotlin") {
    outputs.cacheIf {
        // このタスクの出力をキャッシュしない条件
        !isCI()
    }
}

// インクリメンタルビルドのサポート
tasks.test {
    inputs.files(sourceSets.main.get().output)
    outputs.dir(layout.buildDirectory.dir("test-results"))
}

4.4 Gradle Wrapper

Gradle Wrapper は、プロジェクト固有の Gradle バージョンを管理するメカニズムです。

# Wrapper スクリプトの生成
gradle wrapper --gradle-version=8.5

# プロジェクトメンバーは常に同じバージョンを使用
./gradlew build
# gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

5. 設定の具体例

5.1 マルチプラットフォーム Kotlin プロジェクト

// build.gradle.kts

plugins {
    kotlin("multiplatform") version "1.9.21"
    id("maven-publish")
}

group = "com.example"
version = "1.0.0"

kotlin {
    jvm {
        jvmToolchain(17)
        compilations.all {
            kotlinOptions {
                freeCompilerArgs += "-Xjsr305=strict"
            }
        }
    }
    
    js {
        browser {
            commonWebpackConfig {
                cssSupport {
                    enabled.set(true)
                }
            }
        }
        nodejs()
    }
    
    native {
        val nativeTarget = when {
            System.getProperty("os.name")!! == "Mac OS X" -> macosX64("native")
            System.getProperty("os.arch") == "aarch64" -> macosArm64("native")
            else -> linuxX64("native")
        }
        
        nativeTarget.apply {
            binaries {
                executable {
                    entryPoint = "main"
                }
            }
        }
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("org.jetbrains.kotlin:kotlin-stdlib")
                implementation("io.ktor:ktor-client-core:2.3.0")
            }
        }
        
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test"))
            }
        }
        
        val jvmMain by getting {
            dependencies {
                implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3")
            }
        }
        
        val jvmTest by getting {
            dependencies {
                implementation("org.junit.jupiter:junit-jupiter:5.9.2")
            }
        }
        
        val jsMain by getting {
            dependencies {
                implementation("io.ktor:ktor-client-js:2.3.0")
            }
        }
    }
}

5.2 Spring Boot アプリケーション

// build.gradle.kts

plugins {
    kotlin("jvm") version "1.9.21"
    kotlin("plugin.spring") version "1.9.21"
    id("org.springframework.boot") version "3.1.5"
    id("io.spring.dependency-management") version "1.1.3"
    id("org.jetbrains.kotlin.plugin.jpa") version "1.9.21"
}

group = "com.example"
version = "1.0.0"
java.sourceCompatibility = JavaVersion.VERSION_17

repositories {
    mavenCentral()
}

dependencies {
    // Spring Boot Starters
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-security")
    
    // Kotlin サポート
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    
    // データベース
    runtimeOnly("org.postgresql:postgresql")
    implementation("org.flywaydb:flyway-core")
    
    // ログ
    implementation("org.springframework.boot:spring-boot-starter-logging")
    
    // テスト
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.springframework.security:spring-security-test")
}

springBoot {
    mainClass.set("com.example.ApplicationKt")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs += "-Xjsr305=strict"
        jvmTarget = "17"
    }
}

tasks.test {
    useJUnitPlatform()
}

5.3 ライブラリプロジェクト

// build.gradle.kts

plugins {
    kotlin("jvm") version "1.9.21"
    `java-library`
    `maven-publish`
    signing
    id("org.jetbrains.dokka") version "1.9.10"
}

group = "com.example"
version = "2.0.0"

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
    
    withSourcesJar()
    withJavadocJar()
}

repositories {
    mavenCentral()
}

dependencies {
    // API dependencies(ライブラリのユーザーに公開される)
    api("org.jetbrains.kotlin:kotlin-stdlib")
    api("org.apache.commons:commons-lang3:3.13.0")
    
    // Implementation dependencies(内部でのみ使用)
    implementation("org.slf4j:slf4j-api:2.0.9")
    implementation("org.json:json:20231013")
    
    // Test dependencies
    testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
    testImplementation("org.assertj:assertj-core:3.24.1")
    testImplementation("org.mockito:mockito-core:5.2.0")
}

tasks.test {
    useJUnitPlatform()
}

// Dokka ドキュメント生成
tasks.dokkaHtml.configure {
    dokkaSourceSets {
        configureEach {
            moduleName.set("My Library")
            includeNonPublic.set(false)
        }
    }
}

// 出版設定
publishing {
    publications {
        create<MavenPublication>("maven") {
            from(components["java"])
            
            pom {
                name.set("My Awesome Library")
                description.set("A powerful library for...")
                url.set("https://github.com/example/library")
                
                licenses {
                    license {
                        name.set("MIT License")
                        url.set("https://opensource.org/licenses/MIT")
                    }
                }
                
                developers {
                    developer {
                        id.set("author")
                        name.set("John Doe")
                        email.set("john@example.com")
                    }
                }
                
                scm {
                    url.set("https://github.com/example/library")
                    connection.set("scm:git:https://github.com/example/library.git")
                    developerConnection.set("scm:git:ssh://git@github.com/example/library.git")
                }
            }
        }
    }
    
    repositories {
        maven {
            name = "OSSRH"
            url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
            credentials {
                username = System.getenv("OSSRH_USERNAME")
                password = System.getenv("OSSRH_PASSWORD")
            }
        }
    }
}

// 署名設定
signing {
    sign(publishing.publications["maven"])
}

6. 依存関係管理

6.1 依存関係の構成(Configuration)

Gradle では、依存関係を異なるコンテキストに対して指定できます:

dependencies {
    // コンパイル時に必要
    implementation("org.jetbrains.kotlin:kotlin-stdlib")
    
    // コンパイル時に必要だが、ユーザーに公開されない
    compileOnly("org.projectlombok:lombok:1.18.30")
    
    // テスト実行時のみ必要
    testImplementation("junit:junit:4.13.2")
    
    // テストのコンパイル時のみ必要
    testCompileOnly("org.mockito:mockito-core:5.2.0")
    
    // 実行時のみ必要
    runtimeOnly("mysql:mysql-connector-java:8.0.33")
    
    // ライブラリの API に公開される
    api("com.google.guava:guava:32.1.3-jre")
    
    // アノテーション処理
    annotationProcessor("com.google.dagger:dagger-compiler:2.47")
}

6.2 バージョン管理

// gradle/libs.versions.toml
[versions]
kotlin = "1.9.21"
junit = "5.9.2"
spring-boot = "3.1.5"

[libraries]
kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" }
junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" }
spring-boot-web = { group = "org.springframework.boot", name = "spring-boot-starter-web", version.ref = "spring-boot" }

[bundles]
junit = ["junit-api", "junit-params", "junit-engine"]
spring-boot = ["spring-boot-web", "spring-boot-security"]

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }
// build.gradle.kts
dependencies {
    implementation(libs.kotlin.stdlib)
    testImplementation(libs.bundles.junit)
    implementation(libs.bundles.spring.boot)
}

plugins {
    alias(libs.plugins.kotlin.jvm)
    alias(libs.plugins.spring.boot)
}

6.3 依存関係の制限と除外

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web") {
        // 不要な依存関係を除外
        exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
    }
    
    // バージョン競合の解決
    implementation("com.google.guava:guava") {
        version {
            strictly("32.1.3-jre")
        }
    }
    
    // バージョン範囲の指定
    implementation("org.slf4j:slf4j-api:[1.7,1.8)")
}

// グローバルな除外設定
configurations.all {
    exclude(group = "commons-logging")
    resolutionStrategy {
        force("org.slf4j:slf4j-api:2.0.9")
    }
}

6.4 リポジトリの管理

repositories {
    // 公式 Maven リポジトリ
    mavenCentral()
    
    // Google のリポジトリ
    google()
    
    // カスタムリポジトリ
    maven {
        url = uri("https://repo.example.com/maven")
        credentials {
            username = System.getenv("REPO_USERNAME")
            password = System.getenv("REPO_PASSWORD")
        }
        authentication {
            create<BasicAuthentication>("basic")
        }
    }
    
    // Maven Local(ローカルキャッシュ)
    mavenLocal()
}

// リポジトリの順序は重要:最初にマッチしたものが使用される
repositories {
    gradlePluginPortal()
    google()
    mavenCentral()
}

7. マルチプロジェクト構成

7.1 プロジェクト構造

my-application/
├── settings.gradle.kts
├── build.gradle.kts (ルート)
├── app/
│   └── build.gradle.kts
├── core/
│   ├── build.gradle.kts
│   ├── src/main/kotlin/
│   └── src/test/kotlin/
├── api/
│   └── build.gradle.kts
└── gradle/
    ├── wrapper/
    │   └── gradle-wrapper.properties
    └── libs.versions.toml

7.2 settings.gradle.kts の設定

// settings.gradle.kts

rootProject.name = "my-application"

// サブプロジェクトの定義
include(
    "app",
    "core",
    "api"
)

// プロジェクトディレクトリのカスタマイズ
project(":api").projectDir = file("modules/api")

// グローバル設定
pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

// コンフィグレーション
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")

7.3 ルート build.gradle.kts

// build.gradle.kts (ルート)

plugins {
    kotlin("jvm") version "1.9.21" apply false
    id("org.springframework.boot") version "3.1.5" apply false
}

group = "com.example"
version = "1.0.0"

// すべてのサブプロジェクトに共通の設定を適用
subprojects {
    apply(plugin = "kotlin")
    
    repositories {
        google()
        mavenCentral()
    }
    
    extensions.configure<KotlinProjectExtension> {
        jvmToolchain(17)
    }
}

// 特定のサブプロジェクトにのみ適用
configure(listOf(project(":app"), project(":core"))) {
    apply(plugin = "org.springframework.boot")
    
    dependencies {
        implementation("org.springframework.boot:spring-boot-starter")
    }
}

// タスク
tasks.register("printProjects") {
    group = "help"
    description = "Print all project names"
    doLast {
        rootProject.allprojects.forEach { project ->
            println("Project: ${project.name}")
        }
    }
}

7.4 サブプロジェクトの設定

// app/build.gradle.kts

plugins {
    id("org.springframework.boot")
    id("io.spring.dependency-management")
    kotlin("plugin.spring")
}

dependencies {
    // 同じマルチプロジェクト内の他のプロジェクトに依存
    implementation(project(":core"))
    implementation(project(":api"))
    
    // 外部ライブラリ
    implementation("org.springframework.boot:spring-boot-starter-web")
}

springBoot {
    mainClass.set("com.example.app.AppKt")
}
// core/build.gradle.kts

plugins {
    kotlin("jvm")
    `java-library`
}

dependencies {
    api("org.slf4j:slf4j-api:2.0.9")
    implementation("org.jetbrains.kotlin:kotlin-stdlib")
}

8. パフォーマンス最適化

8.1 ビルドキャッシング

// build.gradle.kts

tasks.register<JavaCompile>("compileKotlin") {
    // タスク出力のキャッシング
    outputs.cacheIf {
        // CI 環境ではキャッシュ無効
        !isCI()
    }
}

// インクリメンタルビルドの有効化
tasks.withType<KotlinCompile>().configureEach {
    kotlinOptions {
        incremental = true
    }
}

// ビルドキャッシュの確認
gradle.addBuildListener(object : BuildListener {
    override fun buildFinished(result: BuildResult) {
        println("Build cache info:")
        // キャッシュ統計の出力
    }
})

8.2 並列実行と最適化

# gradle.properties

# 並列実行を有効化
org.gradle.parallel=true

# ワーカーの最大数を指定
org.gradle.workers.max=8

# デーモンプロセスを有効化(ビルドの高速化)
org.gradle.daemon=true

# Java メモリ設定
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m

# 設定キャッシング
org.gradle.configuration-cache=true

8.3 タスク出力のキャッシング

// build.gradle.kts

tasks.register<Copy>("copyDistribution") {
    from("src/dist")
    into("build/dist")
    
    // このタスクの出力をキャッシュ
    outputs.cacheIf {
        // 条件付きキャッシング
        !isCI()
    }
}

// カスタムタスクの作成
abstract class GenerateConfig : DefaultTask() {
    @get:InputFile
    abstract val config: RegularFileProperty
    
    @get:OutputFile
    abstract val output: RegularFileProperty
    
    @TaskAction
    fun generate() {
        // 処理
    }
}

tasks.register<GenerateConfig>("generateConfig") {
    config.set(project.layout.projectDirectory.file("config.json"))
    output.set(project.layout.buildDirectory.file("generated/config.properties"))
}

8.4 ビルド時間の測定

// build.gradle.kts

import java.time.Instant

class TimingListener : BuildListener {
    private val startTime = Instant.now()
    
    override fun buildFinished(result: BuildResult) {
        val duration = java.time.Duration.between(startTime, Instant.now())
        println("Build completed in ${duration.seconds}s")
    }
}

gradle.addListener(TimingListener())

// タスク実行時間の測定
tasks.withType<Task>().configureEach {
    val taskStartTime = System.currentTimeMillis()
    
    doLast {
        val duration = (System.currentTimeMillis() - taskStartTime) / 1000
        println("${this.name}: ${duration}s")
    }
}

9. ベストプラクティス

9.1 ビルドスクリプト設計

// build.gradle.kts

// 1. プラグインは最初に
plugins {
    kotlin("jvm") version "1.9.21"
    id("org.springframework.boot") version "3.1.5"
}

// 2. プロジェクトメタデータ
group = "com.example"
version = "1.0.0"
description = "Example application"

// 3. Java/Kotlin 設定
java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

// 4. リポジトリ
repositories {
    google()
    mavenCentral()
}

// 5. 依存関係
dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib")
}

// 6. タスク
tasks {
    // タスク設定
}

9.2 DRY 原則に基づいた設定

// buildSrc/src/main/kotlin/Versions.kt
object Versions {
    const val KOTLIN = "1.9.21"
    const val JUNIT = "5.9.2"
    const val SPRING_BOOT = "3.1.5"
}

// buildSrc/src/main/kotlin/Dependencies.kt
object Dependencies {
    const val KOTLIN_STDLIB = "org.jetbrains.kotlin:kotlin-stdlib:${Versions.KOTLIN}"
    const val JUNIT_API = "org.junit.jupiter:junit-jupiter-api:${Versions.JUNIT}"
    const val SPRING_WEB = "org.springframework.boot:spring-boot-starter-web:${Versions.SPRING_BOOT}"
}

// build.gradle.kts
dependencies {
    implementation(Dependencies.KOTLIN_STDLIB)
    testImplementation(Dependencies.JUNIT_API)
    implementation(Dependencies.SPRING_WEB)
}

9.3 カスタムプラグインの作成

// buildSrc/src/main/kotlin/CustomPlugin.kt

import org.gradle.api.Plugin
import org.gradle.api.Project

class CustomPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.tasks.register("myCustomTask") {
            description = "A custom task"
            group = "custom"
            
            doLast {
                println("Custom task executed")
            }
        }
        
        // 拡張クラスの登録
        project.extensions.create("customConfig", CustomExtension::class.java)
    }
}

class CustomExtension {
    var property: String = "default"
}

9.4 条件付きビルド設定

// build.gradle.kts

val isRelease = project.property("release").toString().toBoolean()
val isCI = System.getenv("CI") != null

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib")
    
    // 開発環境のみ
    if (!isRelease) {
        debugImplementation("com.squareup.leakcanary:leakcanary-android:2.12")
    }
}

tasks.test {
    // CI 環境では詳細ログを有効化
    if (isCI) {
        testLogging {
            exceptionFormat = "full"
            showStandardStreams = true
        }
    }
}

10. トラブルシューティング

10.1 一般的な問題と解決方法

問題 1: メモリ不足エラー

java.lang.OutOfMemoryError: Java heap space

解決策:

# gradle.properties
org.gradle.jvmargs=-Xmx4096m -XX:+UseG1GC

問題 2: 依存関係競合

Execution failed for task ':dependencies'.
> Could not resolve all dependencies

解決策:

// build.gradle.kts

// 依存関係の競合を確認
tasks.register("dependencyTree") {
    doLast {
        exec {
            commandLine("gradle", "dependencies")
        }
    }
}

// 競合を解決
configurations.all {
    resolutionStrategy {
        force("org.slf4j:slf4j-api:2.0.9")
        exclude(group = "commons-logging")
    }
}

問題 3: ビルドキャッシュが古い

Build cache contains stale outputs

解決策:

# ビルドキャッシュをクリア
gradle cleanBuildCache

# プロジェクトの再設定
gradle clean build --refresh-dependencies

10.2 デバッグ情報の取得

# 詳細ログを有効化
gradle build --debug

# タスク依存関係を表示
gradle build --dry-run

# ビルド情報を表示
gradle build --info

# スタックトレースを表示
gradle build -s

# プロフィリング情報を取得
gradle build --profile

# タスクグラフを可視化
gradle taskTree
// build.gradle.kts でのデバッグ

// タスク実行前後のログ
gradle.taskGraph.whenReady {
    allTasks.forEach { task ->
        task.doFirst {
            println("Executing task: ${task.name}")
        }
        task.doLast {
            println("Completed task: ${task.name}")
        }
    }
}

// 依存関係の詳細表示
tasks.register("debugDependencies") {
    doLast {
        configurations.forEach { config ->
            println("Configuration: ${config.name}")
            config.dependencies.forEach { dep ->
                println("  - ${dep}")
            }
        }
    }
}

10.3 パフォーマンスプロファイリング

# ビルドプロファイルレポート生成
gradle build --profile

# ビルド時間の詳細分析
gradle build -Dorg.gradle.profiler.log=build-profile.log
// build.gradle.kts

// タスク実行時間の追跡
class PerformanceListener : BuildListener {
    private val taskTimings = mutableMapOf<Task, Long>()
    
    override fun taskStarted(task: TaskExecutionListener.TaskStartEvent) {
        taskTimings[task] = System.currentTimeMillis()
    }
    
    override fun taskFinished(task: TaskExecutionListener.TaskFinishEvent) {
        val startTime = taskTimings.remove(task)
        if (startTime != null) {
            val duration = System.currentTimeMillis() - startTime
            if (duration > 1000) { // 1秒以上の遅いタスクのみ表示
                println("SLOW: ${task.name} took ${duration}ms")
            }
        }
    }
}

gradle.addListener(PerformanceListener())

まとめ

Gradle with Kotlin DSL は、モダンな Java/Kotlin プロジェクトのビルドを効率的に管理するための強力なツールです。主なポイントは以下の通りです:

  1. 柔軟性: タスク、プラグイン、拡張関数により、複雑なビルドロジックを簡潔に記述できる
  2. 型安全性: Kotlin DSL の静的型チェックとコード補完により、ビルドスクリプトの品質向上
  3. スケーラビリティ: マルチプロジェクト構成や依存関係管理に適した機能
  4. パフォーマンス: ビルドキャッシング、並列実行、インクリメンタルビルドにより高速化
  5. 保守性: コンベンションとベストプラクティスに従うことで、長期的に保守しやすいビルドスクリプト

Gradle with Kotlin DSL を適切に活用することで、開発チームの生産性向上と品質の維持が実現します。