Splitting Gradle Script Guide Using Kts

Gradle Script Splitting Guide

Quick Overview

Split large .gradle.kts files into smaller, focused scripts using apply(from = "path/to/file.gradle.kts").

Basic Usage

Main Build File

// thorgKotlinMP.build.gradle.kts
plugins { /* ... */ }

// Apply additional scripts
apply(from = "gradle-scripts/test-tasks.gradle.kts")
apply(from = "gradle-scripts/compile-tasks.gradle.kts")

// Rest of main config
kotlin { /* ... */ }

Included Script

// gradle-scripts/test-tasks.gradle.kts

// ✅ Define tasks
tasks.register("myCustomTask") {
    doLast {
        println("Running in ${project.name}")
    }
}

// ✅ Define helper functions
fun Project.findTestClass(path: String): File? {
    return file("src/test/kotlin/$path").takeIf { it.exists() }
}

// ✅ Access project, tasks, subprojects
subprojects.forEach { /* ... */ }

What Works

FeatureAvailableExample
Define taskstasks.register("myTask") { }
Define functionsfun Project.helper() { }
Define data classesdata class Result(val x: String)
Access projectproject.subprojects
Access taskstasks.getByName("build")
Configure dependenciesdependencies { }
Share variables⚠️Use extra properties (see below)

Sharing Data Between Scripts

Using extra Properties

// Main file
extra["myConfig"] = mapOf("key" to "value")

// Included file
val config = extra["myConfig"] as Map<String, String>

Using Functions (Preferred)

// gradle-scripts/utils.gradle.kts
fun getTestDirs(): List<String> = listOf("src/test/kotlin", "src/jvmTest/kotlin")

// Main file (after apply)
val dirs = getTestDirs()  // ✅ Functions are accessible

Common Pitfalls

❌ Order Matters

// WRONG: Using function before script is applied
val dirs = getTestDirs()
apply(from = "utils.gradle.kts")

// RIGHT: Apply first
apply(from = "utils.gradle.kts")
val dirs = getTestDirs()

❌ Variable Scope

// In included script - these are LOCAL to that script
val myVar = "value"  // NOT accessible in main file

❌ Plugin Configuration

// Included scripts CANNOT apply plugins
// ❌ plugins { id("some-plugin") }  // Error!

// ✅ Apply all plugins in main file only

⚠️ Relative Paths

// Main file at: kotlin-mp/build.gradle.kts
// Included file at: kotlin-mp/gradle-scripts/tasks.gradle.kts

// In included file, paths are relative to PROJECT ROOT (kotlin-mp/), not the script location
val file = file("src/test/kotlin/Foo.kt")  // ✅ Resolves to kotlin-mp/src/test/kotlin/Foo.kt
kotlin-mp/
├── thorgKotlinMP.build.gradle.kts      # Main: plugins, kotlin{}, dependencies{}
├── CommonBuildConfig.gradle.kts         # Shared config (already exists)
└── gradle-scripts/
    ├── test-tasks.gradle.kts            # Test-related tasks (singleJvmTest, etc.)
    ├── test-utils.gradle.kts            # Test helper functions
    ├── compile-tasks.gradle.kts         # Compile tasks (sanityCompile*, etc.)
    └── json-tasks.gradle.kts            # Utility tasks (tasksJson, etc.)

Best Practices

  1. Keep core config in main file: plugins {}, kotlin {}, dependencies {}
  2. One purpose per file: Group related tasks/functions together
  3. Use extension functions: fun Project.helper() for reusability
  4. Apply early: Apply includes near top of main file (after plugins)
  5. Document dependencies: Add comments if one script depends on another

Example Refactor

Before (600+ lines)

// thorgKotlinMP.build.gradle.kts
plugins { }
kotlin { }
// ... 200 lines of config ...
tasks.register("singleJvmTest") { /* 50 lines */ }
tasks.register("singleJvmTestAuto") { /* 80 lines */ }
fun Project.findModule() { /* 30 lines */ }
// ... more tasks ...

After

// thorgKotlinMP.build.gradle.kts (250 lines)
plugins { }
apply(from = "gradle-scripts/test-tasks.gradle.kts")
apply(from = "gradle-scripts/test-utils.gradle.kts")
kotlin { }
dependencies { }

// gradle-scripts/test-tasks.gradle.kts (130 lines)
tasks.register("singleJvmTest") { /* ... */ }
tasks.register("singleJvmTestAuto") { /* ... */ }

// gradle-scripts/test-utils.gradle.kts (30 lines)
data class TestClassLocation(val project: Project, val sourceSet: String)
fun Project.findModule() { /* ... */ }

Debugging Tips

# See evaluation order
./gradlew build --info | grep "Evaluating"

# Check if task is registered
./gradlew tasks --all | grep myTask

# Debug script errors
./gradlew build --stacktrace