rocketraman
02/23/2026, 5:27 PMe: File '/home/raman/source/myproject/shared/build/generated/testBalloon/src/commonTest/kotlin/JvmEntryPoint.kt' can be a part of only one module, but is listed as a source for both `androidUnitTestDebug` and `commonTest`, please check you -Xfragment-sources options.Oliver.O
02/23/2026, 5:59 PMjunit-vintage? If so, does this help?rocketraman
02/23/2026, 6:01 PMOliver.O
02/23/2026, 6:05 PMgradlew clean first make the error disappear?rocketraman
02/23/2026, 6:10 PMclean then build also fails. When running at the terminal rather than in the IDE I see this:
* What went wrong:
A problem was found with the configuration of task ':shared:generateDebugAndroidTestLintModel' (type 'LintModelWriterTask').
- Gradle detected a problem with the following location: '/home/raman/source/devhaus/mobile-sdk/shared/build/generated/testBalloon/src/commonTest'.
Reason: Task ':shared:generateDebugAndroidTestLintModel' uses this output of task ':shared:generateTestBalloonEntryPoint' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.
Possible solutions:
1. Declare task ':shared:generateTestBalloonEntryPoint' as an input of ':shared:generateDebugAndroidTestLintModel'.
2. Declare an explicit dependency on ':shared:generateTestBalloonEntryPoint' from ':shared:generateDebugAndroidTestLintModel' using Task#dependsOn.
3. Declare an explicit dependency on ':shared:generateTestBalloonEntryPoint' from ':shared:generateDebugAndroidTestLintModel' using Task#mustRunAfter.
For more information, please refer to <https://docs.gradle.org/8.14.1/userguide/validation_problems.html#implicit_dependency> in the Gradle documentation.
* Try:
> Declare task ':shared:generateTestBalloonEntryPoint' as an input of ':shared:generateDebugAndroidTestLintModel'
> Declare an explicit dependency on ':shared:generateTestBalloonEntryPoint' from ':shared:generateDebugAndroidTestLintModel' using Task#dependsOn
> Declare an explicit dependency on ':shared:generateTestBalloonEntryPoint' from ':shared:generateDebugAndroidTestLintModel' using Task#mustRunAfter
> Run with --scan to get full insights.rocketraman
02/23/2026, 6:12 PMOliver.O
02/23/2026, 6:16 PMshared module, run testBalloonDiagnostics and send the output (here or via DM)?
afterEvaluate {
val taskName = "testBalloonDiagnostics"
val kotlinMultiplatformExtension =
runCatching { extensions.findByName("kotlin") as? KotlinMultiplatformExtension }.getOrNull()
tasks.register(taskName) {
group = "help"
description = "Shows diagnostics for the TestBalloon test framework"
fun <Result : Any> safeProvider(value: () -> Result?): Provider<Result> = project.provider {
try {
value()
} catch (throwable: Throwable) {
println("WARNING: Problem configuring task step in '$taskName': $throwable")
throwable.printStackTrace()
null
}
}
fun doLastSafely(block: () -> Unit) = doLast {
try {
block()
} catch (throwable: Throwable) {
println("WARNING: Problem executing task step in '$taskName': $throwable")
throwable.printStackTrace()
}
}
val projectPath = project.path
val gradleVersion = gradle.gradleVersion
doLastSafely {
println()
println("--- BEGIN TestBalloon diagnostics for $projectPath ---------------------------------")
println()
println("Project path: $projectPath")
println("Gradle version: $gradleVersion")
}
val buildScriptPluginsBlock = safeProvider {
val buildScriptText = project.buildscript.sourceFile?.readText()
Regex("""plugins \{.*?\}""", option = RegexOption.DOT_MATCHES_ALL).find(buildScriptText ?: "")?.value
?: "no plugins block found in ${project.buildscript.sourceFile?.path ?: "(unknown build script)"}"
}
doLastSafely {
println()
println("Build script plugins block:")
println(buildScriptPluginsBlock.get().prependIndent(" "))
}
val catalogsExtension = safeProvider {
project.extensions.getByType(VersionCatalogsExtension::class.java)
}
val versionCatalogNames = catalogsExtension.map { it.catalogNames.toList() }
val relevantPlugins = safeProvider {
val catalogsExtension = catalogsExtension.get()
val versionCatalogs = catalogsExtension.catalogNames.map { catalogsExtension.named(it) }
val catalogPluginVersions = versionCatalogs.flatMap { catalog ->
catalog.pluginAliases.map {
val (artifactId, version) = catalog.findPlugin(it).get().get().toString().split(':', limit = 2)
artifactId to version
}
}.toMap()
mapOf(
Constants.COMPILER_PLUGIN_NAME to null,
"org.jetbrains.kotlin.jvm" to null,
"org.jetbrains.kotlin.multiplatform" to null,
"com.android.application" to "Sharing a module with KMP is unsupported.",
"com.android.library" to "deprecated",
"org.jetbrains.kotlin.android" to "Must not be used with built-in Kotlin of AGP 9.",
"com.android.kotlin.multiplatform.library" to null
).mapNotNull { (pluginId, notice) ->
pluginManager.findPlugin(pluginId)?.let {
val pluginAndVersion = "$pluginId:${catalogPluginVersions[pluginId]}"
if (notice != null) "$pluginAndVersion (*) $notice" else pluginAndVersion
}
}
}
doLast {
println()
println("Gradle plugins (excerpt, versions from catalog(s) ${versionCatalogNames.get()}):")
println(relevantPlugins.get().joinToString(separator = "\n ", prefix = " "))
}
val relevantProperties = safeProvider {
providers.gradlePropertiesPrefixedBy("testBalloon.").get() +
providers.gradlePropertiesPrefixedBy("org.gradle.").get() +
providers.gradlePropertiesPrefixedBy("kotlin.").get()
}
doLastSafely {
println()
println("Gradle properties (excerpt):")
relevantProperties.get().toSortedMap(String::compareTo).forEach { (name, value) ->
println(" $name=$value")
}
}
val testBalloonVersionsUsage = safeProvider {
configurations
.filter { it.isCanBeResolved }
.flatMap { configuration ->
val visitedComponentIds = mutableSetOf<ComponentIdentifier>()
fun ResolvedComponentResult.withAllChildren(): Sequence<ResolvedComponentResult> = sequence {
if (!visitedComponentIds.add(id)) return@sequence
yield(this@withAllChildren)
dependencies
.filterIsInstance<ResolvedDependencyResult>()
.forEach { dep ->
yieldAll(dep.selected.withAllChildren())
}
}
val rootComponent = configuration.incoming.resolutionResult.rootComponent.get()
val testBalloonVersions = rootComponent.dependencies
.filterIsInstance<ResolvedDependencyResult>()
.flatMap { resolvedDependencyResult ->
resolvedDependencyResult.selected.withAllChildren()
}
.mapNotNull { component ->
(component.id as? ModuleComponentIdentifier)
?.takeIf { it.group == Constants.ARTIFACT_GROUP_ID }
?.let { VersionNumber.parse(it.version) }
}
.toSet()
testBalloonVersions.map {
Pair(it, configuration.name)
}
}
.groupBy({ it.first }) {
it.second
}
}
doLastSafely {
val testBalloonVersionsUsage = testBalloonVersionsUsage.get()
println()
println("TestBalloon dependencies:")
for ((version, configurations) in testBalloonVersionsUsage) {
println(" $version: ${configurations.joinToString(limit = 3)}")
}
when (testBalloonVersionsUsage.size) {
0 -> println(" (*) External TestBalloon dependencies are not present in this module.")
1 -> {}
else -> println(" (*) TestBalloon dependencies with different versions are unsupported and may fail.")
}
}
val testSourceSetsDiagram = safeProvider {
kotlinMultiplatformExtension?.let { kotlin ->
val testSourceSetRegex = Regex("[tT]est")
val testSourceSets = kotlin.sourceSets.filter { testSourceSetRegex.containsMatchIn(it.name) }
buildString {
appendLine()
appendLine("Diagram for display on <https://mermaid.live/>")
appendLine()
appendLine("---")
appendLine("title: KMP test source sets of ${project.path}")
appendLine("---")
appendLine("classDiagram")
for (sourceSet in testSourceSets) {
appendLine(" class ${sourceSet.name}")
}
for (sourceSet in testSourceSets) {
for (dependsOnSourceSet in sourceSet.dependsOn.map { it.name }) {
appendLine(" $dependsOnSourceSet <|-- ${sourceSet.name}")
}
}
}
}
}
doLastSafely {
testSourceSetsDiagram.orNull?.let { print(it) }
}
doLastSafely {
println()
println("--- END TestBalloon diagnostics for $projectPath -----------------------------------")
}
}
}rocketraman
02/23/2026, 6:19 PMConstants?Oliver.O
02/23/2026, 6:21 PMOliver.O
02/23/2026, 6:25 PMafterEvaluate {
val taskName = "testBalloonDiagnostics"
val kotlinMultiplatformExtension =
runCatching { extensions.findByName("kotlin") as? KotlinMultiplatformExtension }.getOrNull()
tasks.register(taskName) {
group = "help"
description = "Shows diagnostics for the TestBalloon test framework"
fun <Result : Any> safeProvider(value: () -> Result?): Provider<Result> = project.provider {
try {
value()
} catch (throwable: Throwable) {
println("WARNING: Problem configuring task step in '$taskName': $throwable")
throwable.printStackTrace()
null
}
}
fun doLastSafely(block: () -> Unit) = doLast {
try {
block()
} catch (throwable: Throwable) {
println("WARNING: Problem executing task step in '$taskName': $throwable")
throwable.printStackTrace()
}
}
val projectPath = project.path
val gradleVersion = gradle.gradleVersion
doLastSafely {
println()
println("--- BEGIN TestBalloon diagnostics for $projectPath ---------------------------------")
println()
println("Project path: $projectPath")
println("Gradle version: $gradleVersion")
}
val buildScriptPluginsBlock = safeProvider {
val buildScriptText = project.buildscript.sourceFile?.readText()
Regex("""plugins \{.*?\}""", option = RegexOption.DOT_MATCHES_ALL).find(buildScriptText ?: "")?.value
?: "no plugins block found in ${project.buildscript.sourceFile?.path ?: "(unknown build script)"}"
}
doLastSafely {
println()
println("Build script plugins block:")
println(buildScriptPluginsBlock.get().prependIndent(" "))
}
val catalogsExtension = safeProvider {
project.extensions.getByType(VersionCatalogsExtension::class.java)
}
val versionCatalogNames = catalogsExtension.map { it.catalogNames.toList() }
val relevantPlugins = safeProvider {
val catalogsExtension = catalogsExtension.get()
val versionCatalogs = catalogsExtension.catalogNames.map { catalogsExtension.named(it) }
val catalogPluginVersions = versionCatalogs.flatMap { catalog ->
catalog.pluginAliases.map {
val (artifactId, version) = catalog.findPlugin(it).get().get().toString().split(':', limit = 2)
artifactId to version
}
}.toMap()
mapOf(
"de.infix.testBalloon" to null,
"org.jetbrains.kotlin.jvm" to null,
"org.jetbrains.kotlin.multiplatform" to null,
"com.android.application" to "Sharing a module with KMP is unsupported.",
"com.android.library" to "deprecated",
"org.jetbrains.kotlin.android" to "Must not be used with built-in Kotlin of AGP 9.",
"com.android.kotlin.multiplatform.library" to null
).mapNotNull { (pluginId, notice) ->
pluginManager.findPlugin(pluginId)?.let {
val pluginAndVersion = "$pluginId:${catalogPluginVersions[pluginId]}"
if (notice != null) "$pluginAndVersion (*) $notice" else pluginAndVersion
}
}
}
doLast {
println()
println("Gradle plugins (excerpt, versions from catalog(s) ${versionCatalogNames.get()}):")
println(relevantPlugins.get().joinToString(separator = "\n ", prefix = " "))
}
val relevantProperties = safeProvider {
providers.gradlePropertiesPrefixedBy("testBalloon.").get() +
providers.gradlePropertiesPrefixedBy("org.gradle.").get() +
providers.gradlePropertiesPrefixedBy("kotlin.").get()
}
doLastSafely {
println()
println("Gradle properties (excerpt):")
relevantProperties.get().toSortedMap(String::compareTo).forEach { (name, value) ->
println(" $name=$value")
}
}
val testBalloonVersionsUsage = safeProvider {
configurations
.filter { it.isCanBeResolved }
.flatMap { configuration ->
val visitedComponentIds = mutableSetOf<ComponentIdentifier>()
fun ResolvedComponentResult.withAllChildren(): Sequence<ResolvedComponentResult> = sequence {
if (!visitedComponentIds.add(id)) return@sequence
yield(this@withAllChildren)
dependencies
.filterIsInstance<ResolvedDependencyResult>()
.forEach { dep ->
yieldAll(dep.selected.withAllChildren())
}
}
val rootComponent = configuration.incoming.resolutionResult.rootComponent.get()
val testBalloonVersions = rootComponent.dependencies
.filterIsInstance<ResolvedDependencyResult>()
.flatMap { resolvedDependencyResult ->
resolvedDependencyResult.selected.withAllChildren()
}
.mapNotNull { component ->
(component.id as? ModuleComponentIdentifier)
?.takeIf { it.group == "de.infix.testBalloon" }
?.let { VersionNumber.parse(it.version) }
}
.toSet()
testBalloonVersions.map {
Pair(it, configuration.name)
}
}
.groupBy({ it.first }) {
it.second
}
}
doLastSafely {
val testBalloonVersionsUsage = testBalloonVersionsUsage.get()
println()
println("TestBalloon dependencies:")
for ((version, configurations) in testBalloonVersionsUsage) {
println(" $version: ${configurations.joinToString(limit = 3)}")
}
when (testBalloonVersionsUsage.size) {
0 -> println(" (*) External TestBalloon dependencies are not present in this module.")
1 -> {}
else -> println(" (*) TestBalloon dependencies with different versions are unsupported and may fail.")
}
}
val testSourceSetsDiagram = safeProvider {
kotlinMultiplatformExtension?.let { kotlin ->
val testSourceSetRegex = Regex("[tT]est")
val testSourceSets = kotlin.sourceSets.filter { testSourceSetRegex.containsMatchIn(it.name) }
buildString {
appendLine()
appendLine("Diagram for display on <https://mermaid.live/>")
appendLine()
appendLine("---")
appendLine("title: KMP test source sets of ${project.path}")
appendLine("---")
appendLine("classDiagram")
for (sourceSet in testSourceSets) {
appendLine(" class ${sourceSet.name}")
}
for (sourceSet in testSourceSets) {
for (dependsOnSourceSet in sourceSet.dependsOn.map { it.name }) {
appendLine(" $dependsOnSourceSet <|-- ${sourceSet.name}")
}
}
}
}
}
doLastSafely {
testSourceSetsDiagram.orNull?.let { print(it) }
}
doLastSafely {
println()
println("--- END TestBalloon diagnostics for $projectPath -----------------------------------")
}
}
}rocketraman
02/23/2026, 6:32 PMrocketraman
02/23/2026, 7:01 PMcom.android.library to the new multiplatform plugin com.android.kotlin.multiplatform.library (see https://developer.android.com/kotlin/multiplatform/plugin).