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.lang.reflect.InvocationTargetException
|
||||
import java.net.{ URL, URLClassLoader }
|
||||
import java.net.URL
|
||||
import java.util.concurrent.{ ExecutorService, Executors }
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import sbt.plugins.{ CorePlugin, IvyPlugin, JvmPlugin }
|
||||
import sbt.util.LogExchange
|
||||
|
|
@ -93,28 +92,19 @@ private[sbt] class XMainConfiguration {
|
|||
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
|
||||
// in this file use the scala library.
|
||||
val modifiedLoader = new URLClassLoader(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 modifiedLoader = new XMainClassLoader(urlArray, topLoader)
|
||||
val xMainConfigurationClass = modifiedLoader.loadClass("sbt.internal.XMainConfiguration")
|
||||
val instance: AnyRef =
|
||||
xMainConfigurationClass.getConstructor().newInstance().asInstanceOf[AnyRef]
|
||||
|
||||
val method = xMainConfigurationClass.getMethod("makeLoader", classOf[AppProvider])
|
||||
val modifiedConfigurationClass =
|
||||
modifiedLoader.loadClass("sbt.internal.XMainConfiguration$ModifiedConfiguration")
|
||||
val metaBuildLoaderClass = modifiedLoader.loadClass("sbt.internal.MetaBuildLoader")
|
||||
val method = metaBuildLoaderClass.getMethod("makeLoader", classOf[AppProvider])
|
||||
|
||||
val loader = method.invoke(instance, configuration.provider).asInstanceOf[ClassLoader]
|
||||
val loader = method.invoke(null, configuration.provider).asInstanceOf[ClassLoader]
|
||||
|
||||
Thread.currentThread.setContextClassLoader(loader)
|
||||
val modifiedConfigurationClass =
|
||||
modifiedLoader.loadClass("sbt.internal.XMainConfiguration$ModifiedConfiguration")
|
||||
val cons = modifiedConfigurationClass.getConstructors()(0)
|
||||
close(configuration.provider.loader)
|
||||
val scalaProvider = configuration.provider.scalaProvider
|
||||
|
|
@ -187,84 +177,4 @@ private[sbt] class XMainConfiguration {
|
|||
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