From efcda3881ae1cf8238b5c7adb5f8ed41f53490af Mon Sep 17 00:00:00 2001 From: rinekri Date: Tue, 31 Mar 2026 13:45:23 +0100 Subject: [PATCH] Fix directory resolving for windows os, use lazy providers --- CHANGELOG.md | 6 ++ gradle.properties | 2 +- .../kotlin/ru/kode/way/gradle/WayPlugin.kt | 63 ++++++++++++++----- .../gradle/WayPluginSourceResolutionTest.kt | 17 +++++ 4 files changed, 72 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 016aca9..4794fd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.9.7 - 2026-03-31 + +* Fix Windows-incompatible Android source discovery in `way-gradle-plugin` by avoiding `SourceDirectorySet.directories` snapshot/provider placeholder paths (e.g. `provider(?)`) +* Rework source input wiring to stay provider/lazy-based until task execution, resolving only real source directories for generation +* Keep existing generation contract and generated output layout while restoring reliable `*Schema`/`*NodeBuilder` generation across Android modules + ## 0.9.6 - 2026-03-26 * Rework `way-gradle-plugin` Android source wiring to use typed Gradle/AGP APIs instead of reflection-based source set registration diff --git a/gradle.properties b/gradle.properties index ebd186b..72ece43 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ kotlin.native.ignoreDisabledTargets=true android.useAndroidX=true -versionName=0.9.6 +versionName=0.9.7 pomGroupId=ru.kode pomDescription=Navigation library based on statechart-like node graphs pomUrl=https://kode.ru diff --git a/way-gradle-plugin/src/main/kotlin/ru/kode/way/gradle/WayPlugin.kt b/way-gradle-plugin/src/main/kotlin/ru/kode/way/gradle/WayPlugin.kt index 3aca134..863cde9 100644 --- a/way-gradle-plugin/src/main/kotlin/ru/kode/way/gradle/WayPlugin.kt +++ b/way-gradle-plugin/src/main/kotlin/ru/kode/way/gradle/WayPlugin.kt @@ -6,6 +6,7 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.tasks.TaskProvider import org.gradle.util.GradleVersion +import org.gradle.api.provider.Provider import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin @@ -83,12 +84,14 @@ class WayPlugin : Plugin { private fun configureTask(task: GenerateClassesTask, sources: List) { task.group = "way" + task.include("**/*.dot") sources.forEach { source -> - val waySourceDirs = source.sourceDirectories.map { sourceDirectory -> - sourceDirectory.resolveWaySourceDir(source.name) + val waySourceDirs = source.sourceDirectories.map { sourceDirectories -> + sourceDirectories.map { sourceDirectory -> + sourceDirectory.resolveWaySourceDir(source.name) + } } task.source(waySourceDirs) - task.include("**/*.dot") } } @@ -100,7 +103,7 @@ class WayPlugin : Plugin { return listOf( Source( name = "commonMain", - sourceDirectories = commonMain.kotlin.srcDirs.toList(), + sourceDirectories = providers.provider { commonMain.kotlin.srcDirs.toList() }, registerGeneratedDir = { taskProvider -> commonMain.kotlin.srcDir(taskProvider) }, @@ -112,10 +115,13 @@ class WayPlugin : Plugin { // Android project val androidExtension = project.extensions.findByType(CommonExtension::class.java) if (androidExtension != null) { - // AGP sourceSet.kotlin.directories API changed semantics across versions. - // Kotlin main source set srcDirs is stable and points to src/main/kotlin|java. - val sourceDirectories = findAndroidSourceDirectories(androidExtension) - if (sourceDirectories.isNotEmpty()) { + // Read resolved source roots from srcDirs; directories snapshots can contain + // provider placeholders (e.g. provider(?)) in AGP built-in Kotlin setups. + val sourceDirectories = findAndroidSourceDirectories( + androidExtension = androidExtension, + kotlinExtension = project.extensions.findByType(KotlinProjectExtension::class.java), + ) + if (androidExtension.sourceSets.any { sourceSet -> !isWayTestSourceSet(sourceSet.name) }) { val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) if (androidComponents != null) { return listOf( @@ -158,7 +164,7 @@ class WayPlugin : Plugin { return listOf( Source( name = "main", - sourceDirectories = mainSourceSet.kotlin.srcDirs.toList(), + sourceDirectories = providers.provider { mainSourceSet.kotlin.srcDirs.toList() }, registerGeneratedDir = { taskProvider -> mainSourceSet.kotlin.srcDir(taskProvider) }, @@ -177,7 +183,7 @@ class WayPlugin : Plugin { return listOf( Source( name = "commonTest", - sourceDirectories = commonTest.kotlin.srcDirs.toList(), + sourceDirectories = providers.provider { commonTest.kotlin.srcDirs.toList() }, registerGeneratedDir = { taskProvider -> commonTest.kotlin.srcDir(taskProvider) }, @@ -188,22 +194,31 @@ class WayPlugin : Plugin { return emptyList() } - private fun Project.findAndroidSourceDirectories(androidExtension: CommonExtension): List = + private fun Project.findAndroidSourceDirectories( + androidExtension: CommonExtension, + kotlinExtension: KotlinProjectExtension?, + ): Provider> = providers.provider { androidExtension.sourceSets .asSequence() .filterNot { sourceSet -> isWayTestSourceSet(sourceSet.name) } .flatMap { sourceSet -> - sequenceOf(sourceSet.kotlin, sourceSet.java) - .flatMap { sourceDirectorySet -> sourceDirectorySet.directories.asSequence() } + collectAndroidSourceDirectories( + kotlinSourceDirectories = kotlinExtension + ?.sourceSets + ?.findByName(sourceSet.name) + ?.kotlin + ?.srcDirs, + javaSourceDirectories = sourceSet.java.directories.asSequence().map(::file).toList(), + ).asSequence() } - .map(::file) .distinct() .toList() + } } private data class Source( val name: String, - val sourceDirectories: List, + val sourceDirectories: Provider>, val registerGeneratedDir: (TaskProvider) -> Unit = {}, ) @@ -226,6 +241,24 @@ internal fun File.resolveWaySourceDir(sourceSetName: String): File = when (name) internal fun isWayTestSourceSet(sourceSetName: String): Boolean = sourceSetName.startsWith("test", ignoreCase = true) || sourceSetName.startsWith("androidTest", ignoreCase = true) +internal fun collectAndroidSourceDirectories( + kotlinSourceDirectories: Collection?, + javaSourceDirectories: Collection?, +): List = ( + resolveAndroidLanguageSourceDirectories( + sourceDirectories = kotlinSourceDirectories, + ) + + resolveAndroidLanguageSourceDirectories( + sourceDirectories = javaSourceDirectories, + ) + ).distinct() + +internal fun resolveAndroidLanguageSourceDirectories( + sourceDirectories: Collection?, +): List = sourceDirectories + .orEmpty() + .distinct() + internal fun registerGeneratedDirInKotlinMainSourceSet( kotlinExtension: KotlinProjectExtension?, taskProvider: TaskProvider, diff --git a/way-gradle-plugin/src/test/kotlin/ru/kode/way/gradle/WayPluginSourceResolutionTest.kt b/way-gradle-plugin/src/test/kotlin/ru/kode/way/gradle/WayPluginSourceResolutionTest.kt index d4fb619..c7e8a23 100644 --- a/way-gradle-plugin/src/test/kotlin/ru/kode/way/gradle/WayPluginSourceResolutionTest.kt +++ b/way-gradle-plugin/src/test/kotlin/ru/kode/way/gradle/WayPluginSourceResolutionTest.kt @@ -36,6 +36,23 @@ class WayPluginSourceResolutionTest : isWayTestSourceSet("release") shouldBe false } + should("collect resolved android source dirs when they are valid") { + val kotlinDir = File("/tmp/proj-android/feature/login/routing/custom/kotlin") + val javaDir = File("/tmp/proj-android/feature/login/routing/custom/java") + + collectAndroidSourceDirectories( + kotlinSourceDirectories = listOf(kotlinDir), + javaSourceDirectories = listOf(javaDir), + ) shouldBe listOf(kotlinDir, javaDir) + } + + should("return empty source dirs when resolved dirs are unavailable") { + collectAndroidSourceDirectories( + kotlinSourceDirectories = emptyList(), + javaSourceDirectories = null, + ) shouldBe emptyList() + } + should("register generated dir in kotlin main source set without reflection") { val project = ProjectBuilder.builder().build() project.pluginManager.apply("org.jetbrains.kotlin.jvm")