This implements plain run for worker for testing.
This commit is contained in:
Eugene Yokota 2025-05-10 23:19:35 -04:00
parent c3baa55668
commit eb74554ec1
7 changed files with 220 additions and 11 deletions

View File

@ -530,6 +530,27 @@ lazy val testAgentProj = (project in file("testing") / "agent")
mimaSettings,
)
lazy val workerProj = (project in file("worker"))
.dependsOn(exampleWorkProj % Test)
.settings(
name := "worker",
testedBaseSettings,
Compile / doc / javacOptions := Nil,
autoScalaLibrary := false,
libraryDependencies += gson,
libraryDependencies += "org.scala-lang" %% "scala3-library" % scalaVersion.value % Test,
// run / fork := false,
Test / fork := true,
)
.configure(addSbtIOForTest)
lazy val exampleWorkProj = (project in file("internal") / "example-work")
.settings(
minimalSettings,
name := "example work",
publish / skip := true,
)
// Basic task engine
lazy val taskProj = (project in file("tasks"))
.dependsOn(collectionProj, utilControl)
@ -656,6 +677,8 @@ lazy val actionsProj = (project in file("main-actions"))
utilLogging,
utilRelation,
utilTracking,
workerProj,
protocolProj,
)
.settings(
testedBaseSettings,
@ -666,18 +689,9 @@ lazy val actionsProj = (project in file("main-actions"))
baseDirectory.value / "src" / "main" / "contraband-scala",
Compile / generateContrabands / sourceManaged := baseDirectory.value / "src" / "main" / "contraband-scala",
Compile / generateContrabands / contrabandFormatsForType := ContrabandConfig.getFormats,
// Test / fork := true,
Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat,
mimaSettings,
mimaBinaryIssueFilters ++= Seq(
// Removed unused private[sbt] nested class
exclude[MissingClassProblem]("sbt.Doc$Scaladoc"),
// Removed no longer used private[sbt] method
exclude[DirectMissingMethodProblem]("sbt.Doc.generate"),
exclude[DirectMissingMethodProblem]("sbt.compiler.Eval.filesModifiedBytes"),
exclude[DirectMissingMethodProblem]("sbt.compiler.Eval.fileModifiedBytes"),
exclude[DirectMissingMethodProblem]("sbt.Doc.$init$"),
// Added field in nested private[this] class
exclude[ReversedMissingMethodProblem]("sbt.compiler.Eval#EvalType.sourceName"),
),
)
.dependsOn(lmCore)
.configure(
@ -1231,6 +1245,7 @@ def allProjects =
lmCoursier,
lmCoursierShaded,
lmCoursierShadedPublishing,
workerProj,
) ++ lowerUtilProjects
// These need to be cross published to 2.12 and 2.13 for Zinc

View File

@ -0,0 +1,9 @@
package example
class Hello
object Hello:
def main(args: Array[String]): Unit =
if args.toList == List("boom") then sys.error("boom")
else println(s"${args.mkString}")
end Hello

View File

@ -58,6 +58,7 @@ object Dependencies {
}
def addSbtIO = addSbtModule(sbtIoPath, "io", sbtIO)
def addSbtIOForTest = addSbtModule(sbtIoPath, "io", sbtIO, Some(Test))
def addSbtCompilerInterface = addSbtModule(sbtZincPath, "compilerInterface", compilerInterface)
def addSbtCompilerClasspath = addSbtModule(sbtZincPath, "zincClasspath", compilerClasspath)
@ -91,6 +92,7 @@ object Dependencies {
val templateResolverApi = "org.scala-sbt" % "template-resolver" % "0.1"
val remoteapis =
"com.eed3si9n.remoteapis.shaded" % "shaded-remoteapis-java" % "2.3.0-M1-52317e00d8d4c37fa778c628485d220fb68a8d08"
val gson = "com.google.code.gson" % "gson" % "2.13.1"
val scalaCompiler = "org.scala-lang" %% "scala3-compiler" % scala3
val scala3Library = "org.scala-lang" %% "scala3-library" % scala3

View File

@ -0,0 +1,13 @@
package sbt.internal.worker1;
import java.net.URI;
public class FilePath {
public URI path;
public String digest;
public FilePath(URI path, String digest) {
this.path = path;
this.digest = digest;
}
}

View File

@ -0,0 +1,43 @@
/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.worker1;
import java.util.ArrayList;
public class RunInfo {
public class JvmRunInfo {
public ArrayList<String> args;
public ArrayList<FilePath> classpath;
public String mainClass;
public boolean connectInput;
public JvmRunInfo(
ArrayList<String> args,
ArrayList<FilePath> classpath,
String mainClass,
boolean connectInput) {
this.args = args;
this.classpath = classpath;
this.mainClass = mainClass;
this.connectInput = connectInput;
}
}
public class NativeRunInfo {}
public boolean jvm;
public JvmRunInfo jvmRunInfo;
public NativeRunInfo nativeRunInfo;
public RunInfo(boolean jvm, JvmRunInfo jvmRunInfo, NativeRunInfo nativeRunInfo) {
this.jvm = jvm;
this.jvmRunInfo = jvmRunInfo;
this.nativeRunInfo = nativeRunInfo;
}
}

View File

@ -0,0 +1,108 @@
/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.worker1;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Scanner;
public final class WorkerMain {
private PrintStream originalOut;
public static void main(final String[] args) throws Exception {
try {
if (args.length == 0) {
WorkerMain app = new WorkerMain();
app.consoleWork();
} else {
System.err.println("missing args");
System.exit(1);
}
} catch (Throwable e) {
e.printStackTrace();
System.exit(1);
}
}
WorkerMain() {
this.originalOut = System.out;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
System.setOut(new PrintStream(baos));
}
void consoleWork() throws Exception {
Scanner input = new Scanner(System.in);
if (input.hasNextLine()) {
String line = input.nextLine();
process(line);
}
}
void process(String json) throws Exception {
JsonElement elem = JsonParser.parseString(json);
JsonObject o = elem.getAsJsonObject();
if (!o.has("jsonrpc")) {
throw new RuntimeException("missing jsonprc element");
}
long id = o.getAsJsonPrimitive("id").getAsLong();
String method = o.getAsJsonPrimitive("method").getAsString();
JsonObject params = o.getAsJsonObject("params");
switch (method) {
case "run":
Gson g = new Gson();
RunInfo info = g.fromJson(params, RunInfo.class);
run(info);
break;
}
}
void run(RunInfo info) throws Exception {
if (info.jvm) {
if (info.jvmRunInfo == null) {
throw new RuntimeException("missing jvmRunInfo element");
}
RunInfo.JvmRunInfo jvmRunInfo = info.jvmRunInfo;
URL[] urls =
jvmRunInfo
.classpath
.stream()
.map(
filePath -> {
try {
return filePath.path.toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
})
.toArray(URL[]::new);
URLClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
try {
Class<?> mainClass = cl.loadClass(jvmRunInfo.mainClass);
Method mainMethod = mainClass.getMethod("main", String[].class);
String[] mainArgs = jvmRunInfo.args.stream().toArray(String[]::new);
mainMethod.invoke(null, (Object) mainArgs);
} finally {
cl.close();
}
} else {
throw new RuntimeException("only jvm is supported");
}
}
}

View File

@ -0,0 +1,19 @@
package sbt.internal.worker1
import sbt.io.IO
object WorkerTest extends verify.BasicTestSuite:
val main = WorkerMain()
test("process") {
val u0 = IO.classLocationPath(classOf[example.Hello]).toUri()
val u1 = IO.classLocationPath(classOf[scala.quoted.Quotes]).toUri()
val u2 = IO.classLocationPath(classOf[scala.AnyVal]).toUri()
val cp =
s"""[{ "path": "${u0}", "digest": "" }, { "path": "${u1}", "digest": "" }, { "path": "${u2}", "digest": "" }]"""
val runInfo =
s"""{ "jvm": true, "jvmRunInfo": { "args": ["hi"], "classpath": $cp, "mainClass": "example.Hello" } }"""
val json = s"""{ "jsonrpc": "2.0", "id": 1, "method": "run", "params": $runInfo }"""
main.process(json)
}
end WorkerTest