Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .bazelignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ scip-gradle-plugin/target/
scip-java/target/
scip-javac/target/
scip-kotlinc/target/
scip-snapshots/target/
scip-snapshots/cases/java/common/target/
scip-snapshots/cases/kotlin/common/target/
target/
tests/buildTools/target/
tests/minimized/target/
tests/snapshots/target/
tests/unit/target/
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,13 @@ jobs:
- name: scip-kotlinc tests
run: nix develop --command sbt scipKotlinc/test

- name: Kotlin snapshots
run: nix develop --command sbt scipKotlincMinimized/kotlincSnapshots
- name: Regenerate snapshots
run: nix develop --command sbt scipSnapshots/run

- name: Check snapshot drift
run: |
git diff --exit-code \
scip-kotlinc/minimized/src/generatedSnapshots
scip-snapshots/expected

maven:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ VERSION
scip-gradle-plugin/gradle
/aspects/

tests/snapshots/META-INF/
scip-snapshots/META-INF/

# scip-kotlinc kctfork-based tests run kotlinc with our plugin, which
# writes META-INF/scip/sources/Test.kt.scip relative to the test
Expand Down
106 changes: 32 additions & 74 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,7 @@ lazy val cli = project
(Compile / mainClass) := Some("com.sourcegraph.scip_java.ScipJava"),
(run / baseDirectory) := (ThisBuild / baseDirectory).value,
// ScipJava.main can call System.exit, so we always fork the JVM when
// sbt invokes it directly (e.g. from the scip-kotlinc snapshots
// task) so it cannot kill the surrounding sbt process.
// sbt invokes it directly so it cannot kill the surrounding sbt process.
Compile / run / fork := true,
Test / fork := true,
// Our CI set up is a couple of measly vCPUs so parallelising tests there makes
Expand Down Expand Up @@ -242,14 +241,6 @@ lazy val cli = project
)
.dependsOn(scip)

// Task key for regenerating the SCIP golden snapshots emitted by
// the scip-kotlinc compiler plugin over the Kotlin minimized fixtures.
// We deliberately do NOT call this `snapshots` to avoid colliding with the
// existing top-level `snapshots` test project (`lazy val snapshots = project`).
lazy val kotlincSnapshots = taskKey[Unit](
"Run the SCIP snapshot generator over the scip-kotlinc minimized project"
)

// The scip-kotlinc compiler plugin. Built as a fat-jar that is later
// embedded into the scip-java CLI distribution (see cli's resourceGenerators)
// so the runtime no longer needs to fetch a published scip-kotlinc
Expand Down Expand Up @@ -335,14 +326,12 @@ lazy val scipKotlinc = project
)
.dependsOn(scipShared)

// `scipKotlincMinimized` mirrors the (still-present) Gradle build at
// scip-kotlinc/minimized/build.gradle.kts. It compiles a small set of
// Kotlin and Java fixtures with the assembled `scipKotlinc` plugin
// attached to kotlinc/javac, producing *.scip files under
// target/scip-targetroot/ which are then converted to SCIP and rendered
// as the human-readable golden snapshots by the `snapshots` task.
lazy val scipKotlincMinimized = project
.in(file("scip-kotlinc/minimized"))
// Kotlin snapshot case. The fixture includes Java sources as interop
// consumers, but the case is still keyed by the Kotlin compiler/plugin version
// axis. It writes *.scip shards under target/scip-targetroot/ for the central
// scipSnapshots project to aggregate and compare with goldens.
lazy val scipSnapshotsKotlinCommon = project
.in(file("scip-snapshots/cases/kotlin/common"))
.enablePlugins(KotlinPlugin)
.settings(
publish / skip := true,
Expand Down Expand Up @@ -391,57 +380,17 @@ lazy val scipKotlincMinimized = project
val tgtRoot = target.value / "scip-targetroot"
s"-Xplugin:scip -sourceroot:${srcRoot.getAbsolutePath} " +
s"-targetroot:${tgtRoot.getAbsolutePath}"
},
// ----- snapshots regeneration task -----
// Invokes `com.sourcegraph.scip_java.ScipJava.main` twice in the cli JVM
// (forked — ScipJava.main calls System.exit on failure). First pass
// converts the *.scip files under target/scip-targetroot/
// into an index.scip; second pass renders that index as the human-readable
// golden snapshots.
//
// We use `kotlincSnapshots` instead of `snapshots` to avoid colliding
// with the existing top-level `snapshots` test project.
kotlincSnapshots :=
Def
.taskDyn {
val srcRoot = (ThisBuild / baseDirectory).value.getAbsolutePath
val tgtRoot = (target.value / "scip-targetroot").getAbsolutePath
val snapDir =
(baseDirectory.value / "src" / "generatedSnapshots" / "resources")
.getAbsolutePath
// Write the aggregated index OUTSIDE the scanned targetroot. If it
// lived under `tgtRoot`, a second `kotlincSnapshots` run would feed
// the previous index.scip back into `aggregate`, which re-applies the
// package prefix and yields doubled symbols
// (e.g. `scip-java maven . . scip-java maven . . kotlin/`).
val indexDir = target.value / "scip-index"
IO.createDirectory(indexDir)
val scipOut = (indexDir / "index.scip").getAbsolutePath
val mainCls = "com.sourcegraph.scip_java.ScipJava"
Def.sequential(
Compile / compile,
(cli / Compile / runMain).toTask(
s" $mainCls aggregate --no-emit-inverse-relationships --cwd $srcRoot --output $scipOut $tgtRoot"
),
(cli / Compile / runMain).toTask(
s" $mainCls snapshot --cwd $srcRoot --output $snapDir ${indexDir
.getAbsolutePath}"
)
)
}
.value
}
)

lazy val minimized = project
.in(file("tests/minimized/.j11"))
lazy val scipSnapshotsJavaCommon = project
.in(file("scip-snapshots/cases/java/common"))
.settings(
publish / skip := true,
run / fork := true,
(Compile / unmanagedSourceDirectories) +=
file("tests/minimized/src/main/java").getAbsoluteFile,
libraryDependencies += "org.projectlombok" % "lombok" % "1.18.22",
// Fork javac so it receives real file paths instead of sbt's `vf://` virtual-file URIs
// (see the comment on `scipKotlincMinimized` for the long story).
// (see the comment on `scipSnapshotsKotlinCommon` for the long story).
javaHome := Some(file(System.getProperty("java.home"))),
// Keep minimized snapshots stable across JDK 11/17/21.
Compile / javacOptions ++= Seq("--release", "11"),
Expand All @@ -466,8 +415,8 @@ def javacModuleOptions = List(
"-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"
)

lazy val snapshots = project
.in(file("tests/snapshots"))
lazy val scipSnapshots = project
.in(file("scip-snapshots"))
.settings(
publish / skip := true,
Test / fork := true,
Expand All @@ -487,18 +436,27 @@ lazy val snapshots = project
.dependsOn(cli)

// Runtime paths for the snapshot generator, passed as -D system properties.
// Depending on `minimized/compile` here guarantees a fresh targetroot whenever
// `snapshots/test` or `snapshots/run` evaluate javaOptions.
// Depending on each snapshot case's compile task guarantees fresh targetroots
// whenever `scipSnapshots/test` or `scipSnapshots/run` evaluate javaOptions.
def snapshotPathOptions = Def.task {
val _ = (minimized / Compile / compile).value
val _java = (scipSnapshotsJavaCommon / Compile / compile).value
val _kotlin = (scipSnapshotsKotlinCommon / Compile / compile).value
val snapshotRoot = (ThisBuild / baseDirectory).value / "scip-snapshots"
Seq(
s"-Dsnapshot.expectDir=${((Compile / sourceDirectory).value / "generated")
.getAbsolutePath}",
s"-Dsnapshot.minimizedTargetroot=${(
minimized / Compile / semanticdbTargetRoot
s"-Dsnapshot.sourceroot=${(ThisBuild / baseDirectory).value.getAbsolutePath}",
"-Dsnapshot.cases=java-common,kotlin-common",
s"-Dsnapshot.case.java-common.expectDir=${(
snapshotRoot / "expected" / "java" / "common"
).getAbsolutePath}",
s"-Dsnapshot.case.java-common.targetroot=${(
scipSnapshotsJavaCommon / Compile / semanticdbTargetRoot
).value.getAbsolutePath}",
s"-Dsnapshot.sourceroot=${(ThisBuild / baseDirectory)
.value
.getAbsolutePath}"
s"-Dsnapshot.case.kotlin-common.expectDir=${(
snapshotRoot / "expected" / "kotlin" / "common"
).getAbsolutePath}",
s"-Dsnapshot.case.kotlin-common.targetroot=${((
scipSnapshotsKotlinCommon / target
).value / "scip-targetroot").getAbsolutePath}",
"-Dsnapshot.case.kotlin-common.aggregateNoEmitInverseRelationships=true"
)
}
8 changes: 4 additions & 4 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,9 @@
# real-world project layouts that must not be reformatted here.
find . -name '*.java' \
-not -path './examples/*' \
-not -path './tests/minimized/*' \
-not -path './tests/snapshots/*' \
-not -path './scip-snapshots/cases/*' \
-not -path './scip-snapshots/expected/*' \
-not -path './scip-java/src/test/resources/fixtures/*' \
-not -path './scip-kotlinc/minimized/*' \
-exec ${pkgs.google-java-format}/bin/google-java-format --dry-run --set-exit-if-changed {} +
touch $out
'';
Expand All @@ -71,7 +70,8 @@
# build-tool test fixtures (real-world project layouts): neither may
# be reformatted.
find . -name '*.kt' \
-not -path './scip-kotlinc/minimized/*' \
-not -path './scip-snapshots/cases/*' \
-not -path './scip-snapshots/expected/*' \
-not -path './scip-java/src/test/resources/fixtures/*' \
-exec ${pkgs.ktfmt}/bin/ktfmt --kotlinlang-style --dry-run --set-exit-if-changed {} +
touch $out
Expand Down
24 changes: 24 additions & 0 deletions scip-snapshots/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# SCIP snapshots

This directory contains end-to-end snapshot fixtures for `scip-javac`,
`scip-kotlinc`, and future mixed Java/Kotlin cases.

## Layout

```text
cases/
java/common/ Java fixtures compiled with `--release 11`.
kotlin/common/ Kotlin fixtures, including Java interop consumers.
mixed/ Reserved for first-class mixed-language integration cases.

expected/
java/common/ Golden snapshots for `cases/java/common`.
kotlin/common/ Golden snapshots for `cases/kotlin/common`.
```

`common` cases should stay stable across the supported host JDK matrix. Add
version-specific cases when a fixture needs newer language features, for example
`cases/java/release-17`, `cases/java/release-21`, or `cases/kotlin/kotlin-2.2`.

Run `sbt scipSnapshots/test` to compare goldens and `sbt scipSnapshots/run` to
regenerate them.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,42 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.scip_code.scip.Document;
import org.scip_code.scip.Index;

/**
* Indexes the {@code tests/minimized} corpus and renders golden SCIP snapshots. Runtime paths are
* supplied as {@code -Dsnapshot.*} system properties by the sbt build (see build.sbt), replacing
* the former sbt-buildinfo generated values.
* Indexes the {@code scip-snapshots/cases} corpora and renders golden SCIP snapshots. Runtime paths
* are supplied as {@code -Dsnapshot.*} system properties by the sbt build (see build.sbt),
* replacing the former sbt-buildinfo generated values.
*/
public class MinimizedSnapshotScipGenerator {
public static final class SnapshotCase {
public final String id;
public final Path expectDirectory;
public final Path targetroot;
public final boolean aggregateNoEmitInverseRelationships;

private SnapshotCase(
String id,
Path expectDirectory,
Path targetroot,
boolean aggregateNoEmitInverseRelationships) {
this.id = id;
this.expectDirectory = expectDirectory;
this.targetroot = targetroot;
this.aggregateNoEmitInverseRelationships = aggregateNoEmitInverseRelationships;
}

public SnapshotContext context() {
return new SnapshotContext(id, expectDirectory);
}
}

public void run(List<String> args) {
int exit = ScipJava.app.run(args);
Expand All @@ -28,25 +51,34 @@ public void run(List<String> args) {
}
}

public void run(SnapshotContext context, SnapshotHandler handler) {
onTargetroot(context, handler, requiredPathProperty("snapshot.minimizedTargetroot"));
public void run(SnapshotCase snapshotCase, SnapshotHandler handler) {
onTargetroot(
snapshotCase.context(),
handler,
snapshotCase.targetroot,
snapshotCase.aggregateNoEmitInverseRelationships);
}

public void onTargetroot(SnapshotContext context, SnapshotHandler handler, Path targetroot) {
public void onTargetroot(
SnapshotContext context,
SnapshotHandler handler,
Path targetroot,
boolean aggregateNoEmitInverseRelationships) {
Path sourceroot = requiredPathProperty("snapshot.sourceroot");
Path scipTempDir = createTempDirectory();
Path snapshotOutput = createTempDirectory();
try {
Path scipOutput = scipTempDir.resolve("index.scip");
run(
Arrays.asList(
"aggregate",
"--cwd",
sourceroot.toString(),
"--output",
scipOutput.toString(),
"--targetroot",
targetroot.toString()));
List<String> aggregateArgs =
new ArrayList<>(
Arrays.asList(
"aggregate", "--cwd", sourceroot.toString(), "--output", scipOutput.toString()));
if (aggregateNoEmitInverseRelationships) {
aggregateArgs.add("--no-emit-inverse-relationships");
}
aggregateArgs.add("--targetroot");
aggregateArgs.add(targetroot.toString());
run(aggregateArgs);
Index index;
try {
index = Index.parseFrom(Files.readAllBytes(scipOutput));
Expand Down Expand Up @@ -78,13 +110,39 @@ public void onTargetroot(SnapshotContext context, SnapshotHandler handler, Path
}
}

public static List<SnapshotCase> snapshotCases() {
List<SnapshotCase> cases =
Arrays.stream(requiredProperty("snapshot.cases").split(","))
.map(String::trim)
.filter(id -> !id.isEmpty())
.map(MinimizedSnapshotScipGenerator::snapshotCase)
.collect(Collectors.toList());
if (cases.isEmpty()) {
throw new IllegalStateException("Missing snapshot cases in -Dsnapshot.cases");
}
return cases;
}

private static SnapshotCase snapshotCase(String id) {
String prefix = "snapshot.case." + id + ".";
return new SnapshotCase(
id,
requiredPathProperty(prefix + "expectDir"),
requiredPathProperty(prefix + "targetroot"),
Boolean.parseBoolean(System.getProperty(prefix + "aggregateNoEmitInverseRelationships")));
}

public static Path requiredPathProperty(String name) {
return Paths.get(requiredProperty(name));
}

private static String requiredProperty(String name) {
String value = System.getProperty(name);
if (value == null || value.trim().isEmpty()) {
throw new IllegalStateException(
"Missing -D" + name + ". Run via sbt snapshots/test or snapshots/run.");
"Missing -D" + name + ". Run via sbt scipSnapshots/test or scipSnapshots/run.");
}
return Paths.get(value);
return value;
}

private static void runScipSnapshot(Path from, Path to) {
Expand Down
Loading
Loading