From 8483da7faab773878be8a80163a774933c763d88 Mon Sep 17 00:00:00 2001 From: Amp Date: Wed, 4 Nov 2026 13:15:52 +0000 Subject: [PATCH] Keep Kotlin compile configs of composite builds apart The Gradle plugin named each Kotlin config file after the sanitized task path. Task paths are only unique within one build, so the root build and an included build both wrote -compileKotlin.txt and the later one replaced the earlier, dropping its sources from the index. Sanitizing also mapped :a-b:compileKotlin and :a:b:compileKotlin to the same name. Name the file after a hash of the build's root directory and the task path instead. Amp-Thread-ID: https://ampcode.com/threads/T-01a1a6a2-f2b0-775c-a192-e5d11290c493 Co-authored-by: Christoph Hegemann --- .../scip_java/gradle/ScipGradlePlugin.java | 15 +++++++++++++-- .../src/test/kotlin/tests/GradleBuildToolTest.kt | 5 +++++ .../gradle/kotlin-composite/a-b/build.gradle | 3 +++ .../a-b/src/main/kotlin/ab/Dashed.kt | 2 ++ .../gradle/kotlin-composite/a/b/build.gradle | 3 +++ .../a/b/src/main/kotlin/ab/Nested.kt | 2 ++ .../fixtures/gradle/kotlin-composite/build.gradle | 12 ++++++++++++ .../gradle/kotlin-composite/included/build.gradle | 6 ++++++ .../kotlin-composite/included/settings.gradle | 1 + .../included/src/main/kotlin/included/Included.kt | 2 ++ .../gradle/kotlin-composite/settings.gradle | 5 +++++ .../kotlin-composite/src/main/kotlin/root/Root.kt | 2 ++ 12 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a-b/build.gradle create mode 100644 scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a-b/src/main/kotlin/ab/Dashed.kt create mode 100644 scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a/b/build.gradle create mode 100644 scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a/b/src/main/kotlin/ab/Nested.kt create mode 100644 scip-java/src/test/resources/fixtures/gradle/kotlin-composite/build.gradle create mode 100644 scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/build.gradle create mode 100644 scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/settings.gradle create mode 100644 scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/src/main/kotlin/included/Included.kt create mode 100644 scip-java/src/test/resources/fixtures/gradle/kotlin-composite/settings.gradle create mode 100644 scip-java/src/test/resources/fixtures/gradle/kotlin-composite/src/main/kotlin/root/Root.kt diff --git a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java index e4c9845b9..76f0e1674 100644 --- a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java +++ b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java @@ -1,11 +1,14 @@ package org.scip_code.scip_java.gradle; import java.io.File; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.MessageDigest; import java.util.ArrayList; import java.util.HashSet; +import java.util.HexFormat; import java.util.List; import java.util.Map; import java.util.Set; @@ -271,7 +274,8 @@ private static Set addJavacPlugin(Project project, Object javacPluginDep } /** - * Dumps the sources and classpath of a Kotlin compile task to {@code kotlin-configs/.txt}. + * Dumps the sources and classpath of a Kotlin compile task to {@code + * kotlin-configs/-.txt}. */ private static void writeKotlinConfig(Task task, String sourceRoot, String targetRoot) { try { @@ -317,7 +321,14 @@ private static void writeKotlinConfig(Task task, String sourceRoot, String targe Path configDir = Paths.get(targetRoot, "kotlin-configs"); Files.createDirectories(configDir); - String fileName = task.getPath().replaceAll("[^A-Za-z0-9]", "-") + ".txt"; + // Task paths are only unique within one build, and every build of a + // composite (included builds, buildSrc) writes here, so the build's + // root directory is part of the identity. Hashing, unlike sanitizing, + // keeps ":a-b:x" and ":a:b:x" apart; NUL cannot occur in a path. + String identity = task.getProject().getRootDir().getAbsolutePath() + "\0" + task.getPath(); + byte[] hash = + MessageDigest.getInstance("SHA-256").digest(identity.getBytes(StandardCharsets.UTF_8)); + String fileName = task.getName() + "-" + HexFormat.of().formatHex(hash, 0, 8) + ".txt"; Files.write(configDir.resolve(fileName), lines); } catch (Exception exc) { throw new RuntimeException( diff --git a/scip-java/src/test/kotlin/tests/GradleBuildToolTest.kt b/scip-java/src/test/kotlin/tests/GradleBuildToolTest.kt index cdf0f6bbf..43473973c 100644 --- a/scip-java/src/test/kotlin/tests/GradleBuildToolTest.kt +++ b/scip-java/src/test/kotlin/tests/GradleBuildToolTest.kt @@ -204,6 +204,11 @@ class GradleBuildToolTest : BuildToolHarness() { runs = 2, ) ) + // Regression test: Kotlin compile tasks whose paths are equal in different + // builds of a composite (`:compileKotlin` in the root and included build), + // or equal after sanitizing (`:a-b` and `:a:b`), overwrote each other's + // configs, so only one of their sources was indexed. + add(checkGradleBuild("kotlin-composite", "gradle/kotlin-composite", expectedScipFiles = 4)) add( checkGradleBuild( "implementation-deps", diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a-b/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a-b/build.gradle new file mode 100644 index 000000000..4f01298ec --- /dev/null +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a-b/build.gradle @@ -0,0 +1,3 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' +} diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a-b/src/main/kotlin/ab/Dashed.kt b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a-b/src/main/kotlin/ab/Dashed.kt new file mode 100644 index 000000000..6813cdb68 --- /dev/null +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a-b/src/main/kotlin/ab/Dashed.kt @@ -0,0 +1,2 @@ +package ab +object Dashed {} diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a/b/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a/b/build.gradle new file mode 100644 index 000000000..4f01298ec --- /dev/null +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a/b/build.gradle @@ -0,0 +1,3 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' +} diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a/b/src/main/kotlin/ab/Nested.kt b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a/b/src/main/kotlin/ab/Nested.kt new file mode 100644 index 000000000..b733bdce6 --- /dev/null +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/a/b/src/main/kotlin/ab/Nested.kt @@ -0,0 +1,2 @@ +package ab +object Nested {} diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/build.gradle new file mode 100644 index 000000000..d5d67742f --- /dev/null +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/build.gradle @@ -0,0 +1,12 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' version '2.4.0' +} +allprojects { + repositories { + mavenCentral() + } +} +// `scipCompileAll` only runs tasks of the root build. +tasks.named('compileKotlin') { + dependsOn gradle.includedBuild('included').task(':compileKotlin') +} diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/build.gradle new file mode 100644 index 000000000..c1066d9c5 --- /dev/null +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/build.gradle @@ -0,0 +1,6 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' version '2.4.0' +} +repositories { + mavenCentral() +} diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/settings.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/settings.gradle new file mode 100644 index 000000000..6bb0b29ca --- /dev/null +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'included' diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/src/main/kotlin/included/Included.kt b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/src/main/kotlin/included/Included.kt new file mode 100644 index 000000000..2a8247a72 --- /dev/null +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/included/src/main/kotlin/included/Included.kt @@ -0,0 +1,2 @@ +package included +object Included {} diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/settings.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/settings.gradle new file mode 100644 index 000000000..179d8caf4 --- /dev/null +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/settings.gradle @@ -0,0 +1,5 @@ +rootProject.name = 'kotlin-composite' +// `:a-b` and `:a:b` both sanitize to `-a-b-compileKotlin`. +include 'a-b', 'a:b' +// Also has a `:compileKotlin` task. +includeBuild 'included' diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/src/main/kotlin/root/Root.kt b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/src/main/kotlin/root/Root.kt new file mode 100644 index 000000000..c3da0fd1f --- /dev/null +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-composite/src/main/kotlin/root/Root.kt @@ -0,0 +1,2 @@ +package root +object Root {}