mirror of https://github.com/sbt/sbt.git
Use java to implement XMain classloaders
These classloaders which are created if sbt is launched with a legacy launcher (or one that doesn't follow the current classloading hierarchy convention), were implemented in scala, but that meant that they were not parallel capable. I fix that by moving the implementations to java. I also move the static method that creates a MetaBuildLoader into the java class.
This commit is contained in:
parent
8fd10bfb5f
commit
a12bccf4a3
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt.internal;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
|
||||||
|
final class FullScalaLoader extends URLClassLoader {
|
||||||
|
private final String jarString;
|
||||||
|
|
||||||
|
FullScalaLoader(final URL[] scalaRest, final URLClassLoader parent) {
|
||||||
|
super(scalaRest, parent);
|
||||||
|
final StringBuilder res = new StringBuilder();
|
||||||
|
int i = 0;
|
||||||
|
while (i < scalaRest.length) {
|
||||||
|
res.append(scalaRest[i].getPath());
|
||||||
|
res.append(", ");
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
jarString = res.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ScalaClassLoader(jars = " + jarString + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
registerAsParallelCapable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt.internal;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import xsbti.AppProvider;
|
||||||
|
import xsbti.ScalaProvider;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public final class MetaBuildLoader extends URLClassLoader {
|
||||||
|
private final URLClassLoader fullScalaLoader;
|
||||||
|
private final URLClassLoader libraryLoader;
|
||||||
|
private final URLClassLoader interfaceLoader;
|
||||||
|
MetaBuildLoader(
|
||||||
|
final URL[] urls,
|
||||||
|
final URLClassLoader fullScalaLoader,
|
||||||
|
final URLClassLoader libraryLoader,
|
||||||
|
final URLClassLoader interfaceLoader) {
|
||||||
|
super(urls, fullScalaLoader);
|
||||||
|
this.fullScalaLoader = fullScalaLoader;
|
||||||
|
this.libraryLoader = libraryLoader;
|
||||||
|
this.interfaceLoader = interfaceLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SbtMetaBuildClassLoader";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
super.close();
|
||||||
|
fullScalaLoader.close();
|
||||||
|
libraryLoader.close();
|
||||||
|
interfaceLoader.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
ClassLoader.registerAsParallelCapable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rearrange the classloaders so that test-interface is above the scala library. Implemented
|
||||||
|
* without using the scala standard library to minimize classloading.
|
||||||
|
*
|
||||||
|
* @param appProvider the appProvider that needs to be modified
|
||||||
|
* @return a ClassLoader with a URLClassLoader for the test-interface-1.0.jar above the
|
||||||
|
* scala library.
|
||||||
|
*/
|
||||||
|
public static MetaBuildLoader makeLoader(final AppProvider appProvider) throws IOException {
|
||||||
|
final Pattern pattern = Pattern.compile("test-interface-[0-9.]+\\.jar");
|
||||||
|
final File[] cp = appProvider.mainClasspath();
|
||||||
|
final URL[] interfaceURL = new URL[1];
|
||||||
|
final URL[] rest = new URL[cp.length - 1];
|
||||||
|
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int j = 0; // index into rest
|
||||||
|
while (i < cp.length) {
|
||||||
|
final File file = cp[i];
|
||||||
|
if (pattern.matcher(file.getName()).find()) {
|
||||||
|
interfaceURL[0] = file.toURI().toURL();
|
||||||
|
} else {
|
||||||
|
rest[j] = file.toURI().toURL();
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final ScalaProvider scalaProvider = appProvider.scalaProvider();
|
||||||
|
final ClassLoader topLoader = scalaProvider.launcher().topLoader();
|
||||||
|
final TestInterfaceLoader interfaceLoader = new TestInterfaceLoader(interfaceURL, topLoader);
|
||||||
|
final File[] siJars = scalaProvider.jars();
|
||||||
|
final URL[] lib = new URL[1];
|
||||||
|
final URL[] scalaRest = new URL[siJars.length - 1];
|
||||||
|
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int j = 0; // index into scalaRest
|
||||||
|
while (i < siJars.length) {
|
||||||
|
final File file = siJars[i];
|
||||||
|
if (file.getName().equals("scala-library.jar")) {
|
||||||
|
lib[0] = file.toURI().toURL();
|
||||||
|
} else {
|
||||||
|
scalaRest[j] = file.toURI().toURL();
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final ScalaLibraryClassLoader libraryLoader = new ScalaLibraryClassLoader(lib, interfaceLoader);
|
||||||
|
final FullScalaLoader fullScalaLoader = new FullScalaLoader(scalaRest, libraryLoader);
|
||||||
|
return new MetaBuildLoader(rest, fullScalaLoader, libraryLoader, interfaceLoader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt.internal;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
|
||||||
|
class TestInterfaceLoader extends URLClassLoader {
|
||||||
|
TestInterfaceLoader(final URL[] urls, final ClassLoader parent) {
|
||||||
|
super(urls, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SbtTestInterfaceClassLoader(" + getURLs()[0] + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
registerAsParallelCapable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt.internal;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
|
||||||
|
class XMainClassLoader extends URLClassLoader {
|
||||||
|
XMainClassLoader(final URL[] urls, final ClassLoader parent) {
|
||||||
|
super(urls, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
|
if (name.startsWith("sbt.internal.XMainConfiguration")) {
|
||||||
|
synchronized (getClassLoadingLock(name)) {
|
||||||
|
Class<?> result = findLoadedClass(name);
|
||||||
|
if (result == null) result = findClass(name);
|
||||||
|
if (resolve) resolveClass(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.loadClass(name, resolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
registerAsParallelCapable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,9 +9,8 @@ package sbt.internal
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.net.{ URL, URLClassLoader }
|
import java.net.URL
|
||||||
import java.util.concurrent.{ ExecutorService, Executors }
|
import java.util.concurrent.{ ExecutorService, Executors }
|
||||||
import java.util.regex.Pattern
|
|
||||||
|
|
||||||
import sbt.plugins.{ CorePlugin, IvyPlugin, JvmPlugin }
|
import sbt.plugins.{ CorePlugin, IvyPlugin, JvmPlugin }
|
||||||
import sbt.util.LogExchange
|
import sbt.util.LogExchange
|
||||||
|
|
@ -93,28 +92,19 @@ private[sbt] class XMainConfiguration {
|
||||||
val topLoader = configuration.provider.scalaProvider.launcher.topLoader
|
val topLoader = configuration.provider.scalaProvider.launcher.topLoader
|
||||||
// This loader doesn't have the scala library in it so it's critical that none of the code
|
// This loader doesn't have the scala library in it so it's critical that none of the code
|
||||||
// in this file use the scala library.
|
// in this file use the scala library.
|
||||||
val modifiedLoader = new URLClassLoader(urlArray, topLoader) {
|
val modifiedLoader = new XMainClassLoader(urlArray, topLoader)
|
||||||
override def loadClass(name: String, resolve: Boolean): Class[_] = {
|
|
||||||
if (name.startsWith("sbt.internal.XMainConfiguration")) {
|
|
||||||
val clazz = findClass(name)
|
|
||||||
if (resolve) resolveClass(clazz)
|
|
||||||
clazz
|
|
||||||
} else {
|
|
||||||
super.loadClass(name, resolve)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val xMainConfigurationClass = modifiedLoader.loadClass("sbt.internal.XMainConfiguration")
|
val xMainConfigurationClass = modifiedLoader.loadClass("sbt.internal.XMainConfiguration")
|
||||||
val instance: AnyRef =
|
val instance: AnyRef =
|
||||||
xMainConfigurationClass.getConstructor().newInstance().asInstanceOf[AnyRef]
|
xMainConfigurationClass.getConstructor().newInstance().asInstanceOf[AnyRef]
|
||||||
|
|
||||||
val method = xMainConfigurationClass.getMethod("makeLoader", classOf[AppProvider])
|
val metaBuildLoaderClass = modifiedLoader.loadClass("sbt.internal.MetaBuildLoader")
|
||||||
val modifiedConfigurationClass =
|
val method = metaBuildLoaderClass.getMethod("makeLoader", classOf[AppProvider])
|
||||||
modifiedLoader.loadClass("sbt.internal.XMainConfiguration$ModifiedConfiguration")
|
|
||||||
|
|
||||||
val loader = method.invoke(instance, configuration.provider).asInstanceOf[ClassLoader]
|
val loader = method.invoke(null, configuration.provider).asInstanceOf[ClassLoader]
|
||||||
|
|
||||||
Thread.currentThread.setContextClassLoader(loader)
|
Thread.currentThread.setContextClassLoader(loader)
|
||||||
|
val modifiedConfigurationClass =
|
||||||
|
modifiedLoader.loadClass("sbt.internal.XMainConfiguration$ModifiedConfiguration")
|
||||||
val cons = modifiedConfigurationClass.getConstructors()(0)
|
val cons = modifiedConfigurationClass.getConstructors()(0)
|
||||||
close(configuration.provider.loader)
|
close(configuration.provider.loader)
|
||||||
val scalaProvider = configuration.provider.scalaProvider
|
val scalaProvider = configuration.provider.scalaProvider
|
||||||
|
|
@ -187,84 +177,4 @@ private[sbt] class XMainConfiguration {
|
||||||
override def provider(): AppProvider = new ModifiedAppProvider(configuration.provider)
|
override def provider(): AppProvider = new ModifiedAppProvider(configuration.provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Rearrange the classloaders so that test-interface is above the scala library. Implemented
|
|
||||||
* without using the scala standard library to minimize classloading.
|
|
||||||
* @param appProvider the appProvider that needs to be modified
|
|
||||||
* @return a ClassLoader with a URLClassLoader for the test-interface-1.0.jar above the
|
|
||||||
* scala library.
|
|
||||||
*/
|
|
||||||
private[sbt] def makeLoader(appProvider: AppProvider): ClassLoader = {
|
|
||||||
val pattern = Pattern.compile("test-interface-[0-9.]+\\.jar")
|
|
||||||
val cp = appProvider.mainClasspath
|
|
||||||
val interfaceURL = new Array[URL](1)
|
|
||||||
val rest = new Array[URL](cp.length - 1)
|
|
||||||
|
|
||||||
{
|
|
||||||
var i = 0
|
|
||||||
var j = 0 // index into rest
|
|
||||||
while (i < cp.length) {
|
|
||||||
val file = cp(i)
|
|
||||||
if (pattern.matcher(file.getName).find()) {
|
|
||||||
interfaceURL(0) = file.toURI.toURL
|
|
||||||
} else {
|
|
||||||
rest(j) = file.toURI.toURL
|
|
||||||
j += 1
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val scalaProvider = appProvider.scalaProvider
|
|
||||||
val topLoader = scalaProvider.launcher.topLoader
|
|
||||||
class InterfaceLoader extends URLClassLoader(interfaceURL, topLoader) {
|
|
||||||
override def toString: String = "SbtTestInterfaceClassLoader(" + interfaceURL(0) + ")"
|
|
||||||
}
|
|
||||||
val interfaceLoader = new InterfaceLoader
|
|
||||||
val siJars = scalaProvider.jars
|
|
||||||
val lib = new Array[URL](1)
|
|
||||||
val scalaRest = new Array[URL](siJars.length - 1)
|
|
||||||
|
|
||||||
{
|
|
||||||
var i = 0
|
|
||||||
var j = 0 // index into scalaRest
|
|
||||||
while (i < siJars.length) {
|
|
||||||
val file = siJars(i)
|
|
||||||
if (file.getName.equals("scala-library.jar")) {
|
|
||||||
lib(0) = file.toURI.toURL
|
|
||||||
} else {
|
|
||||||
scalaRest(j) = file.toURI.toURL
|
|
||||||
j += 1
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class LibraryLoader extends URLClassLoader(lib, interfaceLoader) {
|
|
||||||
override def toString: String = "ScalaLibraryLoader( " + lib(0) + ")"
|
|
||||||
}
|
|
||||||
val libraryLoader = new LibraryLoader
|
|
||||||
class FullLoader extends URLClassLoader(scalaRest, libraryLoader) {
|
|
||||||
private val jarString: String = {
|
|
||||||
val res = new java.lang.StringBuilder
|
|
||||||
var i = 0
|
|
||||||
while (i < scalaRest.length) {
|
|
||||||
res.append(scalaRest(i).getPath)
|
|
||||||
res.append(", ")
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
res.toString
|
|
||||||
}
|
|
||||||
override def toString: String = "ScalaClassLoader(jars = " + jarString + ")"
|
|
||||||
}
|
|
||||||
val fullLoader = new FullLoader
|
|
||||||
class MetaBuildLoader extends URLClassLoader(rest, fullLoader) {
|
|
||||||
override def toString: String = "SbtMetaBuildClassLoader"
|
|
||||||
override def close(): Unit = {
|
|
||||||
super.close()
|
|
||||||
libraryLoader.close()
|
|
||||||
fullLoader.close()
|
|
||||||
interfaceLoader.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new MetaBuildLoader
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue