The build-logic module contains Gradle convention plugins and shared build configuration for the NoteDelight project. It centralizes common build logic to reduce duplication across module build scripts and enforce consistent configuration.
- Define reusable Gradle convention plugins
- Centralize common build configuration
- Enforce consistent build settings across modules
- Simplify module
build.gradle.ktsfiles - Provide custom build tasks and extensions
build-logic (Gradle Convention Plugins)
├── convention/
│ ├── src/
│ │ └── main/
│ │ └── kotlin/
│ │ ├── GradleConventionPlugin.kt
│ │ └── com/softartdev/notedelight/ProjectExtensions.kt
│ ├── build.gradle.kts
│ └── settings.gradle.kts (not present, uses root settings)
├── gradle.properties
└── settings.gradle.kts
Main convention plugin applied to Gradle builds:
import org.gradle.api.Plugin
import org.gradle.api.Project
class GradleConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
// Configure Gradle-specific settings
configureGradle()
}
}
private fun Project.configureGradle() {
// Gradle wrapper validation
// Build cache configuration
// Common repository configuration
// etc.
}
}Plugins are registered in build.gradle.kts:
gradlePlugin {
plugins {
register("gradleConvention") {
id = "com.softartdev.notedelight.buildlogic.convention"
implementationClass = "GradleConventionPlugin"
}
}
}convention/src/main/kotlin/com/softartdev/notedelight/ProjectExtensions.kt contains reusable extension functions used by module build.gradle.kts files to keep scripts small and focused.
Current extracted helpers include:
disableIosReleaseTasks()for iOS pod release link task disablingexcludeSqliteJdbcFromNonTestConfigurations()for SQLDelight/JVM driver substitutionconfigureWasmJsChromeForKarmaTests()for web Karma Chrome auto-detection (CHROME_BIN)configureWebSqlite3mcWasmResources()for SQLite3MultipleCiphers WASM download/unzip task wiringforceAndroidXDependencyVersions()for Android dependency resolution forcing
Typical module usage:
import com.softartdev.notedelight.forceAndroidXDependencyVersions
plugins {
alias(libs.plugins.gradle.convention)
}
project.forceAndroidXDependencyVersions()Apply convention plugins in module build.gradle.kts:
plugins {
alias(libs.plugins.gradle.convention)
// Other plugins...
}This automatically applies:
- Common Gradle configuration
- Repository setup
- Build cache settings
- Validation rules
// In each module's build.gradle.kts
repositories {
google()
mavenCentral()
// ... repeated everywhere
}
tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "17"
// ... repeated everywhere
}
}// In module's build.gradle.kts
plugins {
alias(libs.plugins.gradle.convention)
}
// That's it! Common config applied automaticallybuild-logic/gradle.properties:
# Convention plugin configuration
kotlin.code.style=official
org.gradle.jvmargs=-Xmx2048m
org.gradle.caching=trueConvention plugins can access version catalog from root:
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
val kotlinVersion = libs.findVersion("kotlin").get()fun Project.configureRepositories() {
repositories {
google()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
}fun Project.configureKotlin() {
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = libs.versions.jdk.get()
freeCompilerArgs += listOf(
"-Xopt-in=kotlin.RequiresOptIn",
"-Xcontext-receivers"
)
}
}
}fun Project.configureTests() {
tasks.withType<Test>().configureEach {
useJUnitPlatform()
testLogging {
events("passed", "skipped", "failed")
}
}
}Enable build cache for faster builds:
fun Project.configureBuildCache() {
gradle.sharedServices.registerIfAbsent("buildCache", BuildCacheService::class.java) {
// Build cache configuration
}
}Define reusable tasks:
abstract class ValidateCodeTask : DefaultTask() {
@TaskAction
fun validate() {
// Custom validation logic
}
}
// Register task
tasks.register<ValidateCodeTask>("validateCode")dependencies {
compileOnly(kotlin("gradle-plugin", version = libs.versions.kotlin.get()))
}This allows convention plugins to configure Kotlin plugin.
Convention plugins are built automatically when building the project:
# Build all (includes build-logic)
./gradlew build
# Build only build-logic
./gradlew :build-logic:convention:build./gradlew :build-logic:convention:testGradle validates plugins at build time:
tasks {
validatePlugins {
enableStricterValidation = true
failOnWarning = true
}
}This ensures:
- Correct plugin implementation
- Proper annotations
- Valid plugin IDs
- No warnings
fun Project.configureForPlatform() {
when {
pluginManager.hasPlugin("com.android.application") -> {
// Android-specific config
}
pluginManager.hasPlugin("org.jetbrains.kotlin.jvm") -> {
// JVM-specific config
}
}
}// Define project extension
fun Project.customProperty(): String {
return findProperty("customProp") as? String ?: "default"
}
// Use in build scripts
val myProp = project.customProperty()interface NoteDelightExtension {
val enableFeatureX: Property<Boolean>
}
// Register extension
project.extensions.create<NoteDelightExtension>("noteDelight")
// Use in build script
noteDelight {
enableFeatureX.set(true)
}- Single responsibility: One plugin per concern
- Composable: Plugins should work together
- Configurable: Allow overrides when needed
- Documented: Document plugin behavior
- Tested: Write tests for plugins
// Good: Concise with convention plugin
plugins {
alias(libs.plugins.gradle.convention)
alias(libs.plugins.kotlin.multiplatform)
}
// Bad: Lots of repeated configuration
plugins {
kotlin("multiplatform")
}
repositories { /* ... */ }
tasks.withType<KotlinCompile> { /* ... */ }
// etc.When working with this module:
- DRY principle: Extract repeated configuration into conventions
- Backwards compatibility: Don't break existing modules
- Testing: Test convention plugins thoroughly
- Documentation: Document plugin behavior clearly
- Versioning: Be careful with breaking changes
- Performance: Keep plugins lightweight
- Validation: Enable strict validation
- Composition: Prefer small, composable plugins
- Type safety: Use Kotlin DSL features
- Error messages: Provide helpful error messages
- Check plugin ID: Verify ID in
libs.versions.toml - Rebuild:
./gradlew clean build - Check classpath: Ensure plugin is on buildscript classpath
- Check order: Plugin order matters
- Check conflicts: Look for conflicting configuration
- Debug: Use
--stacktraceand--infoflags
- Profile builds:
./gradlew build --profile - Enable caching: Configure build cache
- Parallel execution: Use
--parallelflag
# Gradle daemon
org.gradle.daemon=true
org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError
# Build cache
org.gradle.caching=true
# Parallel builds
org.gradle.parallel=true
# Configuration cache
org.gradle.configuration-cache=truePotential convention plugins to add:
- Android Convention: Common Android configuration
- Compose Convention: Compose-specific settings
- Multiplatform Convention: KMP defaults
- Publishing Convention: Maven publishing setup
- Documentation Convention: Dokka configuration
- Code Quality Convention: Detekt, ktlint integration
- Used by: All modules (via
libs.plugins.gradle.convention) - Scope: Build-time only