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 {}