[2.x] Minimalist console (#8722)

**Problem**
Forked console currently pulls in full Zinc, which includes JLine.

**Solution**
This implements a lighter-weight, full Java ForkConsoleMain,
which no longer depends on JLine.
This commit is contained in:
eugene yokota
2026-02-09 10:55:44 -05:00
committed by GitHub
parent 20ce3abe5f
commit edd7061f15
15 changed files with 307 additions and 447 deletions
@@ -0,0 +1,40 @@
/*
* 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.net.URI;
import java.io.Serializable;
import java.util.ArrayList;
public class ConsoleInfo implements Serializable {
public ArrayList<URI> toolsJars;
public ArrayList<URI> bridgeJars;
public ArrayList<URI> products;
public ArrayList<URI> classpathJars;
public ArrayList<String> scalacOptions;
public String initialCommands;
public String cleanupCommands;
public ConsoleInfo(
ArrayList<URI> toolsJars,
ArrayList<URI> bridgeJars,
ArrayList<URI> products,
ArrayList<URI> classpathJars,
ArrayList<String> scalacOptions,
String initialCommands,
String cleanupCommands) {
this.toolsJars = toolsJars;
this.bridgeJars = bridgeJars;
this.products = products;
this.classpathJars = classpathJars;
this.scalacOptions = scalacOptions;
this.initialCommands = initialCommands;
this.cleanupCommands = cleanupCommands;
}
}
@@ -0,0 +1,116 @@
package sbt.internal.worker1;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import xsbti.compile.ConsoleInterface1;
public final class ForkConsoleMain {
public void run(long id, ConsoleInfo info) throws Exception {
try {
Class cls = ConsoleInterface1.class;
Iterator iter = ServiceLoader.load(cls, ForkConsoleMain.class.getClassLoader()).iterator();
List<ConsoleInterface1> list = new ArrayList<>();
while (iter.hasNext()) {
list.add((ConsoleInterface1) iter.next());
}
if (list.size() > 0) {
runInterface1(list.get(0), info);
} else {
runOldInterface(info);
}
} catch (Throwable e) {
e.printStackTrace();
System.exit(1);
}
}
private void runInterface1(ConsoleInterface1 intf, ConsoleInfo info) throws Exception {
String toolsJars =
info.toolsJars
.stream()
.map(u -> Paths.get(u).toString())
.collect(Collectors.joining(File.pathSeparator));
String classpathJars =
Stream.concat(info.products.stream(), info.classpathJars.stream())
.map(u -> Paths.get(u).toString())
.collect(Collectors.joining(File.pathSeparator));
intf.run(
info.scalacOptions.toArray(new String[0]),
toolsJars,
classpathJars,
info.initialCommands,
info.cleanupCommands,
createClassLoader(info, ForkConsoleMain.class.getClassLoader()),
new String[] {},
new Object[] {},
new ZeroLogger());
}
private void runOldInterface(ConsoleInfo info) throws Exception {
Class concrete = Class.forName("xsbt.ConsoleInterface");
Object instance = concrete.getDeclaredConstructor().newInstance();
Method m =
concrete.getMethod(
"run",
String[].class,
String.class,
String.class,
String.class,
String.class,
ClassLoader.class,
String[].class,
Object[].class,
xsbti.Logger.class);
String toolsJars =
info.toolsJars
.stream()
.map(u -> Paths.get(u).toString())
.collect(Collectors.joining(File.pathSeparator));
String classpathJars =
Stream.concat(info.products.stream(), info.classpathJars.stream())
.map(u -> Paths.get(u).toString())
.collect(Collectors.joining(File.pathSeparator));
m.invoke(
instance,
info.scalacOptions.toArray(new String[0]),
toolsJars,
classpathJars,
info.initialCommands,
info.cleanupCommands,
createClassLoader(info, concrete.getClassLoader()),
new String[] {},
new Object[] {},
new ZeroLogger());
}
private URLClassLoader createClassLoader(ConsoleInfo info, ClassLoader parent) {
URL[] urls =
Stream.concat(info.products.stream(), info.classpathJars.stream())
.map(
u -> {
try {
return u.toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
})
.toArray(URL[]::new);
return new URLClassLoader(urls, parent);
}
public static void main(long id, ConsoleInfo info) throws Exception {
new ForkConsoleMain().run(id, info);
}
}
@@ -21,6 +21,9 @@ import java.io.IOException;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
@@ -67,6 +70,10 @@ public final class WorkerMain {
WorkerMain app = new WorkerMain();
app.consoleWork();
System.exit(0);
} else if (args.length == 1 && args[0].startsWith("@")) {
WorkerMain app = new WorkerMain();
app.argFileWork(Paths.get(args[0].substring(1)));
System.exit(0);
} else if (args.length == 2 && args[0].equals("--tcp")) {
WorkerMain app = new WorkerMain();
int serverPort = Integer.parseInt(args[1]);
@@ -99,6 +106,13 @@ public final class WorkerMain {
}
}
void argFileWork(Path arg) throws Exception {
this.jsonOut = this.originalOut;
byte[] encoded = Files.readAllBytes(arg);
String line = new String(encoded, "UTF-8");
process(line);
}
void socketWork(int serverPort) throws Exception {
InetAddress loopback = InetAddress.getByName(null);
Socket client = new Socket(loopback, serverPort);
@@ -132,6 +146,10 @@ public final class WorkerMain {
TestInfo testInfo = g.fromJson(params, TestInfo.class);
test(id, testInfo);
break;
case "console":
ConsoleInfo consoleInfo = g.fromJson(params, ConsoleInfo.class);
console(id, consoleInfo);
return;
case "bye":
break;
}
@@ -178,6 +196,11 @@ public final class WorkerMain {
}
}
void console(long id, ConsoleInfo info) throws Exception {
ForkConsoleMain.main(id, info);
return;
}
private URLClassLoader createClassLoader(RunInfo.JvmRunInfo info, ClassLoader parent) {
URL[] urls =
info.classpath
@@ -0,0 +1,24 @@
/*
* 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.function.Supplier;
import xsbti.Logger;
public class ZeroLogger implements Logger {
public void error(Supplier<String> msg) {}
public void warn(Supplier<String> msg) {}
public void info(Supplier<String> msg) {}
public void debug(Supplier<String> msg) {}
public void trace(Supplier<Throwable> exception) {}
}