Create SubpluginOptions from processor config, fix tests
diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspConfigurations.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspConfigurations.kt
index a4508e8..5423a53 100644
--- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspConfigurations.kt
+++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspConfigurations.kt
@@ -115,6 +115,8 @@
      *    that don't belong to any compilation, like user-defined intermediate source sets (e.g. iosMain).
      *    These do not currently have their own ksp configuration.
      * 2) all* can return sets belonging to other [KotlinCompilation]s
+     *
+     * See test: SourceSetConfigurationsTest.configurationsForMultiplatformApp_doesNotCrossCompilationBoundaries
      */
     fun find(compilation: KotlinCompilation<*>): Set<Configuration> {
         val kotlinConfigurations = compilation.kotlinSourceSets.mapNotNull { kotlinConfigurations[it] }
diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt
index f547623..78ac68d 100644
--- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt
+++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt
@@ -104,7 +104,7 @@
         private fun getSubpluginOptions(
             project: Project,
             kspExtension: KspExtension,
-            nonEmptyKspConfigurations: List<Configuration>,
+            classpath: Configuration,
             sourceSetName: String,
             isIncremental: Boolean,
         ): List<SubpluginOption> {
@@ -121,7 +121,7 @@
                 project.findProperty("ksp.incremental.log")?.toString() ?: "false"
             )
             options += SubpluginOption("projectBaseDir", project.project.projectDir.canonicalPath)
-            options += FilesSubpluginOption("apclasspath", nonEmptyKspConfigurations.flatten())
+            options += FilesSubpluginOption("apclasspath", classpath.toList())
 
             kspExtension.apOptions.forEach {
                 options += SubpluginOption("apoption", "${it.key}=${it.value}")
@@ -186,12 +186,18 @@
         val kotlinCompileTask = kotlinCompileProvider.get()
 
         fun configureAsKspTask(kspTask: KspTask, isIncremental: Boolean) {
+            // depends on the processor; if the processor changes, it needs to be reprocessed.
+            val processorClasspath = project.configurations.maybeCreate("${kspTaskName}ProcessorClasspath")
+                .extendsFrom(*nonEmptyKspConfigurations.toTypedArray())
+            kspTask.processorClasspath.from(processorClasspath)
+            kspTask.dependsOn(processorClasspath.buildDependencies)
+
             kspTask.options.addAll(
                 kspTask.project.provider {
                     getSubpluginOptions(
                         project,
                         kspExtension,
-                        nonEmptyKspConfigurations,
+                        processorClasspath,
                         sourceSetName,
                         isIncremental
                     )
@@ -202,11 +208,6 @@
             kspTask.apOptions.value(kspExtension.arguments).disallowChanges()
             kspTask.kspCacheDir.fileValue(getKspCachesDir(project, sourceSetName)).disallowChanges()
 
-            // depends on the processor; if the processor changes, it needs to be reprocessed.
-            val processorClasspath = project.configurations.maybeCreate("${kspTaskName}ProcessorClasspath")
-                .extendsFrom(*nonEmptyKspConfigurations.toTypedArray())
-            kspTask.processorClasspath.from(processorClasspath)
-            kspTask.dependsOn(processorClasspath.buildDependencies)
 
             if (kspExtension.blockOtherCompilerPlugins) {
                 // FIXME: ask upstream to provide an API to make this not implementation-dependent.
diff --git a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt
index 70b5924..3535133 100644
--- a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt
+++ b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt
@@ -111,7 +111,7 @@
 
         testRule.runner()
             .withDebug(true)
-            .withArguments("app:assemble")
+            .withArguments("app:assemble", "--stacktrace")
             .forwardOutput()
             .build()
     }
diff --git a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/SourceSetConfigurationsTest.kt b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/SourceSetConfigurationsTest.kt
index 02aded2..b754930 100644
--- a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/SourceSetConfigurationsTest.kt
+++ b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/SourceSetConfigurationsTest.kt
@@ -47,8 +47,9 @@
         val result = testRule.runner()
             .withArguments(":app:dependencies")
             .build()
+        val configurations = result.output.lines().map { it.split(' ').first() }
 
-        assertThat(result.output.lines()).containsAtLeast("ksp", "kspTest")
+        assertThat(configurations).containsAtLeast("ksp", "kspTest")
     }
 
     @Test
@@ -58,8 +59,9 @@
         val result = testRule.runner()
             .withArguments(":app:dependencies")
             .build()
+        val configurations = result.output.lines().map { it.split(' ').first() }
 
-        assertThat(result.output.lines()).containsAtLeast(
+        assertThat(configurations).containsAtLeast(
             "ksp",
             "kspAndroidTest",
             "kspAndroidTestDebug",
@@ -87,8 +89,9 @@
         val result = testRule.runner()
             .withArguments(":app:dependencies")
             .build()
+        val configurations = result.output.lines().map { it.split(' ').first() }
 
-        assertThat(result.output.lines()).containsAtLeast(
+        assertThat(configurations).containsAtLeast(
             // jvm target:
             "kspJvm",
             "kspJvmTest",
@@ -115,6 +118,42 @@
     }
 
     @Test
+    fun configurationsForMultiplatformApp_doesNotCrossCompilationBoundaries() {
+        // Adding a ksp dependency on jvmParent should not leak into jvmChild compilation,
+        // even if the source sets depend on each other. This works because we use
+        // KotlinCompilation.kotlinSourceSets instead of KotlinCompilation.allKotlinSourceSets
+        testRule.setupAppAsMultiplatformApp("""
+            kotlin {
+                jvm("jvmParent") { }
+                jvm("jvmChild") { }
+            }
+        """.trimIndent())
+        testRule.appModule.addMultiplatformSource("commonMain", "Foo.kt", "class Foo")
+        testRule.appModule.buildFileAdditions.add("""
+            kotlin {
+                sourceSets {
+                    this["jvmChildMain"].dependsOn(this["jvmParentMain"])
+                }
+            }
+            dependencies {
+                add("kspJvmParent", "androidx.room:room-compiler:2.3.0")
+            }
+            tasks.register("checkConfigurations") {
+                doLast {
+                    // child has no dependencies, so task is not created.
+                    val parent = tasks.findByName("kspKotlinJvmParent")
+                    val child = tasks.findByName("kspKotlinJvmChild")
+                    require(parent != null)
+                    require(child == null)
+                }
+            }
+        """.trimIndent())
+        testRule.runner()
+            .withArguments(":app:checkConfigurations")
+            .build()
+    }
+
+    @Test
     fun registerJavaSourcesToAndroid() {
         testRule.setupAppAsAndroidApp()
         testRule.appModule.dependencies.addAll(
@@ -236,10 +275,11 @@
             .build()
 
         // kaptClasspath_* seem to be intermediate configurations that never run.
-        val kaptConfigurations = result.output.lines().filter {
+        val configurations = result.output.lines().map { it.split(' ').first() }
+        val kaptConfigurations = configurations.filter {
             it.startsWith("kapt") && !it.startsWith("kaptClasspath_")
         }
-        val kspConfigurations = result.output.lines().filter {
+        val kspConfigurations = configurations.filter {
             it.startsWith("ksp")
         }
         assertThat(kspConfigurations).containsExactlyElementsIn(