mirror of https://github.com/sbt/sbt.git
Fix reboot full in RunFromSourceMain
The initial implementation of reboot full did not work correctly. After this change, the project/extra scripted test, which exercises reboot full works.
This commit is contained in:
parent
c47db91bdc
commit
eb66651886
|
|
@ -7,12 +7,13 @@
|
|||
|
||||
package sbt
|
||||
|
||||
import java.io.File.pathSeparator
|
||||
|
||||
import sbt.internal.scriptedtest.ScriptedLauncher
|
||||
import sbt.util.LogExchange
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.sys.process.Process
|
||||
import java.io.File.pathSeparator
|
||||
|
||||
object RunFromSourceMain {
|
||||
def fork(
|
||||
|
|
@ -76,14 +77,16 @@ object RunFromSourceMain {
|
|||
sbtVersion: String,
|
||||
classpath: String,
|
||||
args: Seq[String],
|
||||
): Option[(File, Seq[String])] =
|
||||
try launch(baseDir, scalaVersion, sbtVersion, classpath, args) map exit
|
||||
): Option[(File, Seq[String])] = {
|
||||
try launch(defaultBootDirectory, baseDir, scalaVersion, sbtVersion, classpath, args) map exit
|
||||
catch {
|
||||
case r: xsbti.FullReload => Some((baseDir, r.arguments()))
|
||||
case scala.util.control.NonFatal(e) => e.printStackTrace(); errorAndExit(e.toString)
|
||||
}
|
||||
}
|
||||
|
||||
private def launch(
|
||||
bootDirectory: File,
|
||||
baseDirectory: File,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
|
|
@ -92,7 +95,7 @@ object RunFromSourceMain {
|
|||
): Option[Int] = {
|
||||
ScriptedLauncher
|
||||
.launch(
|
||||
scalaHome(scalaVersion),
|
||||
scalaHome(bootDirectory, scalaVersion),
|
||||
sbtVersion,
|
||||
scalaVersion,
|
||||
bootDirectory,
|
||||
|
|
@ -107,14 +110,15 @@ object RunFromSourceMain {
|
|||
}
|
||||
}
|
||||
|
||||
private lazy val bootDirectory: File = file(sys.props("user.home")) / ".sbt" / "boot"
|
||||
private def scalaHome(scalaVersion: String): File = {
|
||||
private lazy val defaultBootDirectory: File =
|
||||
file(sys.props("user.home")) / ".sbt" / "scripted" / "boot"
|
||||
private def scalaHome(bootDirectory: File, scalaVersion: String): File = {
|
||||
val log = sbt.util.LogExchange.logger("run-from-source")
|
||||
val scalaHome0 = bootDirectory / s"scala-$scalaVersion"
|
||||
if ((scalaHome0 / "lib").exists) scalaHome0
|
||||
else {
|
||||
log.info(s"""scalaHome ($scalaHome0) wasn't found""")
|
||||
val fakeboot = file(sys.props("user.home")) / ".sbt" / "fakeboot"
|
||||
val fakeboot = bootDirectory / "fakeboot"
|
||||
val scalaHome1 = fakeboot / s"scala-$scalaVersion"
|
||||
val scalaHome1Lib = scalaHome1 / "lib"
|
||||
val scalaHome1Temp = scalaHome1 / "temp"
|
||||
|
|
|
|||
|
|
@ -15,26 +15,25 @@ import java.lang.reflect.Method;
|
|||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.FileVisitor;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import xsbti.AppConfiguration;
|
||||
import xsbti.AppMain;
|
||||
import xsbti.AppProvider;
|
||||
import xsbti.ApplicationID;
|
||||
import xsbti.ComponentProvider;
|
||||
import xsbti.Continue;
|
||||
import xsbti.CrossValue;
|
||||
import xsbti.Exit;
|
||||
import xsbti.FullReload;
|
||||
import xsbti.GlobalLock;
|
||||
import xsbti.Launcher;
|
||||
import xsbti.MainResult;
|
||||
import xsbti.Predefined;
|
||||
import xsbti.PredefinedRepository;
|
||||
import xsbti.Reboot;
|
||||
import xsbti.Repository;
|
||||
import xsbti.ScalaProvider;
|
||||
|
||||
|
|
@ -54,26 +53,27 @@ public class ScriptedLauncher {
|
|||
final File bootDirectory,
|
||||
final File baseDir,
|
||||
final File[] classpath,
|
||||
String[] args)
|
||||
throws MalformedURLException, InvocationTargetException, ClassNotFoundException,
|
||||
NoSuchMethodException, IllegalAccessException {
|
||||
String[] arguments)
|
||||
throws InvocationTargetException, ClassNotFoundException, NoSuchMethodException,
|
||||
IllegalAccessException, IOException {
|
||||
String[] args = arguments;
|
||||
Object appID = null;
|
||||
if (System.getProperty("sbt.launch.jar") == null) {
|
||||
while (true) {
|
||||
final URL configURL = URLForClass(xsbti.AppConfiguration.class);
|
||||
final URL mainURL = URLForClass(sbt.xMain.class);
|
||||
final URL scriptedURL = URLForClass(ScriptedLauncher.class);
|
||||
final ClassLoader topLoader = new URLClassLoader(new URL[] {configURL}, top());
|
||||
final URLClassLoader loader =
|
||||
new URLClassLoader(new URL[] {mainURL, scriptedURL}, topLoader);
|
||||
final ClassLoader previous = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
final AtomicInteger result = new AtomicInteger(-1);
|
||||
final AtomicReference<String[]> newArguments = new AtomicReference<>();
|
||||
final ClassLoader previous = Thread.currentThread().getContextClassLoader();
|
||||
final URL configURL = URLForClass(xsbti.AppConfiguration.class);
|
||||
final URL mainURL = URLForClass(sbt.xMain.class);
|
||||
final URL scriptedURL = URLForClass(ScriptedLauncher.class);
|
||||
final ClassLoader topLoader = new URLClassLoader(new URL[] {configURL}, top());
|
||||
final URLClassLoader loader = new URLClassLoader(new URL[] {mainURL, scriptedURL}, topLoader);
|
||||
try {
|
||||
while (true) {
|
||||
final Class<?> clazz = loader.loadClass("sbt.internal.scriptedtest.ScriptedLauncher");
|
||||
final Class<?> reboot = loader.loadClass("xsbti.Reboot");
|
||||
final Class<?> exit = loader.loadClass("xsbti.Exit");
|
||||
|
||||
Method method =
|
||||
clazz.getDeclaredMethod(
|
||||
"launchImpl",
|
||||
ClassLoader.class,
|
||||
"getConf",
|
||||
ClassLoader.class,
|
||||
File.class,
|
||||
String.class,
|
||||
|
|
@ -82,30 +82,56 @@ public class ScriptedLauncher {
|
|||
File.class,
|
||||
File[].class,
|
||||
String[].class,
|
||||
AtomicInteger.class,
|
||||
AtomicReference.class);
|
||||
method.invoke(
|
||||
null,
|
||||
topLoader,
|
||||
loader,
|
||||
scalaHome,
|
||||
sbtVersion,
|
||||
scalaVersion,
|
||||
bootDirectory,
|
||||
baseDir,
|
||||
classpath,
|
||||
args,
|
||||
result,
|
||||
newArguments);
|
||||
final int res = result.get();
|
||||
if (res >= 0) return res == Integer.MAX_VALUE ? Optional.empty() : Optional.of(res);
|
||||
else args = newArguments.get();
|
||||
} catch (final InvocationTargetException e) {
|
||||
if (e.getCause() instanceof RuntimeException) throw (RuntimeException) e.getCause();
|
||||
else throw e;
|
||||
} finally {
|
||||
swap(loader, previous);
|
||||
loader.loadClass("xsbti.ApplicationID"));
|
||||
Thread.currentThread().setContextClassLoader(loader);
|
||||
try {
|
||||
final Object conf =
|
||||
method.invoke(
|
||||
null,
|
||||
topLoader,
|
||||
scalaHome,
|
||||
sbtVersion,
|
||||
scalaVersion,
|
||||
bootDirectory,
|
||||
baseDir,
|
||||
classpath,
|
||||
args,
|
||||
appID);
|
||||
final Object launchResult =
|
||||
clazz
|
||||
.getDeclaredMethod(
|
||||
"launchImpl", ClassLoader.class, ClassLoader.class, Object.class)
|
||||
.invoke(null, topLoader, loader, conf);
|
||||
if (reboot.isAssignableFrom(launchResult.getClass())) {
|
||||
final Object a = reboot.getDeclaredMethod("arguments").invoke(launchResult);
|
||||
final int length = Array.getLength(a);
|
||||
args = new String[length];
|
||||
for (int j = 0; j < length; ++j) {
|
||||
args[j] = (String) Array.get(a, j);
|
||||
}
|
||||
appID = reboot.getDeclaredMethod("app").invoke(launchResult);
|
||||
} else if (exit.isAssignableFrom(launchResult.getClass())) {
|
||||
return Optional.of((Integer) exit.getDeclaredMethod("code").invoke(launchResult));
|
||||
}
|
||||
} catch (final InvocationTargetException e) {
|
||||
Throwable t = e.getCause();
|
||||
while (t != null && !t.getClass().getCanonicalName().equals("xsbti.FullReload"))
|
||||
t = t.getCause();
|
||||
final RuntimeException reload = t == null ? null : (RuntimeException) t;
|
||||
if (reload != null) {
|
||||
final boolean clean =
|
||||
(boolean) reload.getClass().getDeclaredMethod("clean").invoke(reload);
|
||||
if (clean) deleteRecursive(bootDirectory);
|
||||
final Object reloadArgs =
|
||||
reload.getClass().getDeclaredMethod("arguments").invoke(reload);
|
||||
throw new xsbti.FullReload((String[]) reloadArgs, true);
|
||||
}
|
||||
if (e.getCause() instanceof RuntimeException) throw (RuntimeException) e.getCause();
|
||||
throw new RuntimeException(e.getCause());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
swap(loader, previous);
|
||||
}
|
||||
} else {
|
||||
final URL url = new URL("file:" + System.getProperty("sbt.launch.jar"));
|
||||
|
|
@ -141,72 +167,31 @@ public class ScriptedLauncher {
|
|||
Thread.currentThread().setContextClassLoader(stashed);
|
||||
}
|
||||
|
||||
private static void copy(final File[] files, final File toDirectory) {
|
||||
private static boolean copy(final File[] files, final File toDirectory) throws IOException {
|
||||
boolean result = true;
|
||||
for (final File file : files) {
|
||||
try {
|
||||
Files.createDirectories(toDirectory.toPath());
|
||||
Files.copy(file.toPath(), toDirectory.toPath().resolve(file.getName()));
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
} catch (final FileAlreadyExistsException e) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void launchImpl(
|
||||
final ClassLoader topLoader,
|
||||
final ClassLoader loader,
|
||||
final File scalaHome,
|
||||
final String sbtVersion,
|
||||
final String scalaVersion,
|
||||
final File bootDirectory,
|
||||
final File baseDir,
|
||||
final File[] classpath,
|
||||
final String[] args,
|
||||
final AtomicInteger result,
|
||||
final AtomicReference<String[]> newArguments)
|
||||
public static Object launchImpl(
|
||||
final ClassLoader topLoader, final ClassLoader loader, final Object conf)
|
||||
throws ClassNotFoundException, InvocationTargetException, IllegalAccessException,
|
||||
NoSuchMethodException, InstantiationException {
|
||||
final AppConfiguration conf =
|
||||
getConf(
|
||||
topLoader,
|
||||
scalaHome,
|
||||
sbtVersion,
|
||||
scalaVersion,
|
||||
bootDirectory,
|
||||
baseDir,
|
||||
classpath,
|
||||
args);
|
||||
final Class<?> clazz = loader.loadClass("sbt.xMain");
|
||||
final Object instance = clazz.getConstructor().newInstance();
|
||||
final Method run = clazz.getDeclaredMethod("run", loader.loadClass("xsbti.AppConfiguration"));
|
||||
Object runResult;
|
||||
try {
|
||||
runResult = run.invoke(instance, conf);
|
||||
} catch (final InvocationTargetException e) {
|
||||
runResult = e.getCause();
|
||||
}
|
||||
if (runResult instanceof Reboot) newArguments.set(((Reboot) runResult).arguments());
|
||||
else if (runResult instanceof FullReload)
|
||||
newArguments.set(((FullReload) runResult).arguments());
|
||||
else if (runResult instanceof Exit) {
|
||||
result.set(((Exit) runResult).code());
|
||||
} else if (runResult instanceof Continue) {
|
||||
result.set(Integer.MAX_VALUE);
|
||||
} else if (runResult instanceof Throwable) {
|
||||
((Throwable) runResult).printStackTrace(System.err);
|
||||
result.set(1);
|
||||
} else {
|
||||
handleUnknownMainResult((MainResult) runResult);
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleUnknownMainResult(MainResult x) {
|
||||
final String clazz = x == null ? "" : " (class: " + x.getClass() + ")";
|
||||
System.err.println("Invalid main result: " + x + clazz);
|
||||
System.exit(1);
|
||||
return run.invoke(instance, conf);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static AppConfiguration getConf(
|
||||
final ClassLoader topLoader,
|
||||
final File scalaHome,
|
||||
|
|
@ -215,54 +200,111 @@ public class ScriptedLauncher {
|
|||
final File bootDirectory,
|
||||
final File baseDir,
|
||||
final File[] classpath,
|
||||
String[] args) {
|
||||
String[] args,
|
||||
final ApplicationID appID) {
|
||||
|
||||
final File libDir = new File(scalaHome, "lib");
|
||||
final AtomicReference<File[]> classpathExtra = new AtomicReference<>(new File[0]);
|
||||
final ApplicationID id =
|
||||
new ApplicationID() {
|
||||
@Override
|
||||
public String groupID() {
|
||||
return "org.scala-sbt";
|
||||
}
|
||||
appID != null
|
||||
? appID
|
||||
: new ApplicationID() {
|
||||
@Override
|
||||
public String groupID() {
|
||||
return "org.scala-sbt";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "sbt";
|
||||
}
|
||||
@Override
|
||||
public String name() {
|
||||
return "sbt";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String version() {
|
||||
return sbtVersion;
|
||||
}
|
||||
@Override
|
||||
public String version() {
|
||||
return sbtVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mainClass() {
|
||||
return "sbt.xMain";
|
||||
}
|
||||
@Override
|
||||
public String mainClass() {
|
||||
return "sbt.xMain";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] mainComponents() {
|
||||
return new String[] {"xsbti", "extra"};
|
||||
}
|
||||
@Override
|
||||
public String[] mainComponents() {
|
||||
return new String[] {"xsbti", "extra"};
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean crossVersioned() {
|
||||
return false;
|
||||
}
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean crossVersioned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrossValue crossVersionedValue() {
|
||||
return CrossValue.Disabled;
|
||||
}
|
||||
@Override
|
||||
public CrossValue crossVersionedValue() {
|
||||
return CrossValue.Disabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] classpathExtra() {
|
||||
return new File[0];
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public File[] classpathExtra() {
|
||||
return classpathExtra.get();
|
||||
}
|
||||
};
|
||||
final File appHome =
|
||||
scalaHome.toPath().resolve(id.groupID()).resolve(id.name()).resolve(id.version()).toFile();
|
||||
final ComponentProvider provider =
|
||||
new ComponentProvider() {
|
||||
@Override
|
||||
public File componentLocation(String id) {
|
||||
return new File(appHome, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] component(String componentID) {
|
||||
final File dir = componentLocation(componentID);
|
||||
final File[] files = dir.listFiles(File::isFile);
|
||||
return files == null ? new File[0] : files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defineComponent(String componentID, File[] components) {
|
||||
final File dir = componentLocation(componentID);
|
||||
if (dir.exists()) {
|
||||
final StringBuilder files = new StringBuilder();
|
||||
for (final File file : components) {
|
||||
if (files.length() > 0) {
|
||||
files.append(',');
|
||||
}
|
||||
files.append(file.toString());
|
||||
}
|
||||
throw new RuntimeException(
|
||||
"Cannot redefine component. ID: " + id + ", files: " + files);
|
||||
} else {
|
||||
try {
|
||||
copy(components, dir);
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addToComponent(String componentID, File[] components) {
|
||||
try {
|
||||
boolean result = copy(components, componentLocation(componentID));
|
||||
final File[] extra = componentLocation(componentID).listFiles();
|
||||
classpathExtra.set(extra == null ? new File[0] : extra);
|
||||
return result;
|
||||
} catch (final IOException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File lockFile() {
|
||||
return new File(appHome, "sbt.components.lock");
|
||||
}
|
||||
};
|
||||
assert (libDir.exists());
|
||||
final File[] jars = libDir.listFiles(f -> f.isFile() && f.getName().endsWith(".jar"));
|
||||
final URL[] urls = new URL[jars.length];
|
||||
|
|
@ -463,51 +505,43 @@ public class ScriptedLauncher {
|
|||
|
||||
@Override
|
||||
public ComponentProvider components() {
|
||||
return new ComponentProvider() {
|
||||
@Override
|
||||
public File componentLocation(String id) {
|
||||
return new File(appHome, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] component(String componentID) {
|
||||
final File dir = componentLocation(componentID);
|
||||
final File[] files = dir.listFiles(File::isFile);
|
||||
return files == null ? new File[0] : files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defineComponent(String componentID, File[] components) {
|
||||
final File dir = componentLocation(componentID);
|
||||
if (dir.exists()) {
|
||||
final StringBuilder files = new StringBuilder();
|
||||
for (final File file : components) {
|
||||
if (files.length() > 0) {
|
||||
files.append(',');
|
||||
}
|
||||
files.append(file.toString());
|
||||
}
|
||||
throw new RuntimeException(
|
||||
"Cannot redefine component. ID: " + id + ", files: " + files);
|
||||
} else {
|
||||
copy(components, dir);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addToComponent(String componentID, File[] components) {
|
||||
copy(components, componentLocation(componentID));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File lockFile() {
|
||||
return new File(appHome, "sbt.components.lock");
|
||||
}
|
||||
};
|
||||
return provider;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void deleteRecursive(final File directory) {
|
||||
try {
|
||||
Files.walkFileTree(
|
||||
directory.toPath(),
|
||||
new FileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||
throws IOException {
|
||||
if (attrs.isRegularFile()) Files.deleteIfExists(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFileFailed(Path file, IOException exc) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
|
||||
throws IOException {
|
||||
Files.deleteIfExists(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
} catch (final IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue