Merge pull request #6246 from eed3si9n/wip/2.13-pre1

Port XMainConfiguration to Java
This commit is contained in:
eugene yokota 2021-01-10 16:08:02 -05:00 committed by GitHub
commit cb4c1e7100
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 362 additions and 203 deletions

View File

@ -17,6 +17,7 @@ ThisBuild / Test / scalafmtOnCompile := !(Global / insideCI).value
ThisBuild / turbo := true
ThisBuild / usePipelining := false // !(Global / insideCI).value
Global / semanticdbEnabled := !(Global / insideCI).value
val excludeLint = SettingKey[Set[Def.KeyedInitialize[_]]]("excludeLintKeys")
Global / excludeLint := (Global / excludeLint).?.value.getOrElse(Set.empty)
Global / excludeLint += componentID
@ -1029,7 +1030,11 @@ lazy val mainProj = (project in file("main"))
// internal logging apis,
exclude[IncompatibleSignatureProblem]("sbt.internal.LogManager*"),
exclude[MissingTypesProblem]("sbt.internal.RelayAppender"),
exclude[MissingClassProblem]("sbt.internal.TaskProgress$ProgressThread")
exclude[MissingClassProblem]("sbt.internal.TaskProgress$ProgressThread"),
// internal implementation
exclude[MissingClassProblem](
"sbt.internal.XMainConfiguration$ModifiedConfiguration$ModifiedAppProvider$ModifiedScalaProvider$"
),
)
)
.configure(
@ -1317,6 +1322,7 @@ def runNpm(command: String, base: File, log: sbt.internal.util.ManagedLogger) =
lazy val vscodePlugin = (project in file("vscode-sbt-scala"))
.settings(
bspEnabled := false,
crossPaths := false,
crossScalaVersions := Seq(baseScalaVersion),
skip in publish := true,

View File

@ -33,32 +33,32 @@ object InputWrapper {
@compileTimeOnly(
"`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task."
)
def wrapTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
def `wrapTask_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
@compileTimeOnly(
"`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting."
)
def wrapInit_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
def `wrapInit_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
@compileTimeOnly(
"`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task."
)
def wrapInitTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
def `wrapInitTask_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
@compileTimeOnly(
"`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask."
)
def wrapInputTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
def `wrapInputTask_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
@compileTimeOnly(
"`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask."
)
def wrapInitInputTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
def `wrapInitInputTask_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
@compileTimeOnly(
"`previous` can only be called on a task within a task or input task definition macro, such as :=, +=, ++=, Def.task, or Def.inputTask."
)
def wrapPrevious_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
def `wrapPrevious_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
private[this] def implDetailError =
sys.error("This method is an implementation detail and should not be referenced.")
@ -240,13 +240,13 @@ object ParserInput {
@compileTimeOnly(
"`parsed` can only be used within an input task macro, such as := or Def.inputTask."
)
def parser_\u2603\u2603[T](@deprecated("unused", "") i: Any): T =
def `parser_\u2603\u2603`[T](@deprecated("unused", "") i: Any): T =
sys.error("This method is an implementation detail and should not be referenced.")
@compileTimeOnly(
"`parsed` can only be used within an input task macro, such as := or Def.inputTask."
)
def initParser_\u2603\u2603[T](@deprecated("unused", "") i: Any): T =
def `initParser_\u2603\u2603`[T](@deprecated("unused", "") i: Any): T =
sys.error("This method is an implementation detail and should not be referenced.")
private[std] def wrap[T: c.WeakTypeTag](

View File

@ -0,0 +1,299 @@
/*
* 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.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import xsbti.*;
/**
* Generates a new app configuration and invokes xMainImpl.run. For AppConfigurations generated by
* recent launchers, it is unnecessary to modify the original configuration, but configurations
* generated by older launchers need to be modified to place the test interface jar higher in the
* class hierarchy. The methods this object are implemented without using the scala library so that
* we can avoid loading any classes from the old scala provider.
*/
public class XMainConfiguration {
public xsbti.MainResult run(String moduleName, xsbti.AppConfiguration configuration) {
try {
ClassLoader topLoader = configuration.provider().scalaProvider().launcher().topLoader();
xsbti.AppConfiguration updatedConfiguration = null;
try {
Method method = topLoader.getClass().getMethod("getJLineJars");
URL[] jars = (URL[]) method.invoke(topLoader);
boolean canReuseConfiguration = jars.length == 3;
int j = 0;
while (j < jars.length && canReuseConfiguration) {
String s = jars[j].toString();
canReuseConfiguration = s.contains("jline") || s.contains("jansi");
j += 1;
}
if (canReuseConfiguration && j == 3) {
updatedConfiguration = configuration;
} else {
updatedConfiguration = makeConfiguration(configuration);
}
} catch (NoSuchMethodException e) {
updatedConfiguration = makeConfiguration(configuration);
}
ClassLoader loader = updatedConfiguration.provider().loader();
Thread.currentThread().setContextClassLoader(loader);
Class<?> clazz = loader.loadClass("sbt." + moduleName + "$");
Object instance = clazz.getField("MODULE$").get(null);
Method runMethod = clazz.getMethod("run", xsbti.AppConfiguration.class);
try {
Class<?> clw = loader.loadClass("sbt.internal.ClassLoaderWarmup$");
clw.getMethod("warmup").invoke(clw.getField("MODULE$").get(null));
return (xsbti.MainResult) runMethod.invoke(instance, updatedConfiguration);
} catch (InvocationTargetException e) {
// This propogates xsbti.FullReload to the launcher
throw (xsbti.FullReload) e.getCause();
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
private xsbti.AppConfiguration makeConfiguration(xsbti.AppConfiguration configuration) {
try {
ClassLoader baseLoader = XMainConfiguration.class.getClassLoader();
String className = "sbt/internal/XMainConfiguration.class";
URL url = baseLoader.getResource(className);
String path = url.toString().replaceAll(className.concat("$"), "");
URL[] urlArray = new URL[1];
urlArray[0] = new URL(path);
ClassLoader 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.
ClassLoader modifiedLoader = new XMainClassLoader(urlArray, topLoader);
Class<?> xMainConfigurationClass =
modifiedLoader.loadClass("sbt.internal.XMainConfiguration");
Object instance = (Object) xMainConfigurationClass.getConstructor().newInstance();
Class<?> metaBuildLoaderClass = modifiedLoader.loadClass("sbt.internal.MetaBuildLoader");
Method method = metaBuildLoaderClass.getMethod("makeLoader", AppProvider.class);
ClassLoader loader = (ClassLoader) method.invoke(null, configuration.provider());
Thread.currentThread().setContextClassLoader(loader);
Class<?> modifiedConfigurationClass =
modifiedLoader.loadClass("sbt.internal.XMainConfiguration$ModifiedConfiguration");
Constructor<?> cons = modifiedConfigurationClass.getConstructors()[0];
ClassLoaderClose.close(configuration.provider().loader());
ScalaProvider scalaProvider = configuration.provider().scalaProvider();
Class<? extends ScalaProvider> providerClass = scalaProvider.getClass();
try {
Method method2 = providerClass.getMethod("loaderLibraryOnly");
ClassLoaderClose.close((ClassLoader) method2.invoke(scalaProvider));
} catch (NoSuchMethodException e) {
}
ClassLoaderClose.close(scalaProvider.loader());
ClassLoaderClose.close(configuration.provider().loader());
return (xsbti.AppConfiguration) cons.newInstance(instance, configuration, loader);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
/*
* Replaces the AppProvider.loader method with a new loader that puts the sbt test interface
* jar ahead of the rest of the sbt classpath in the classloading hierarchy.
*/
public class ModifiedConfiguration implements xsbti.AppConfiguration {
private xsbti.AppConfiguration configuration;
private ClassLoader metaLoader;
public ModifiedConfiguration(xsbti.AppConfiguration configuration, ClassLoader metaLoader) {
this.configuration = configuration;
this.metaLoader = metaLoader;
}
public class ModifiedAppProvider implements AppProvider {
private AppProvider appProvider;
private ScalaProvider instance;
public ModifiedAppProvider(AppProvider appProvider) throws ClassNotFoundException {
this.appProvider = appProvider;
ScalaProvider delegate = configuration.provider().scalaProvider();
this.instance =
new ScalaProvider() {
public Launcher _launcher =
new Launcher() {
private Launcher delegateLauncher = delegate.launcher();
private ClassLoader interfaceLoader =
metaLoader.loadClass("sbt.testing.Framework").getClassLoader();
@Override
public ScalaProvider getScala(String version) {
return getScala(version, "");
}
@Override
public ScalaProvider getScala(String version, String reason) {
return getScala(version, reason, "org.scala-lang");
}
@Override
public ScalaProvider getScala(String version, String reason, String scalaOrg) {
return delegateLauncher.getScala(version, reason, scalaOrg);
}
@Override
public AppProvider app(xsbti.ApplicationID id, String version) {
return delegateLauncher.app(id, version);
}
@Override
public ClassLoader topLoader() {
return interfaceLoader;
}
@Override
public GlobalLock globalLock() {
return delegateLauncher.globalLock();
}
@Override
public File bootDirectory() {
return delegateLauncher.bootDirectory();
}
@Override
public xsbti.Repository[] ivyRepositories() {
return delegateLauncher.ivyRepositories();
}
@Override
public xsbti.Repository[] appRepositories() {
return delegateLauncher.appRepositories();
}
@Override
public boolean isOverrideRepositories() {
return delegateLauncher.isOverrideRepositories();
}
@Override
public File ivyHome() {
return delegateLauncher.ivyHome();
}
@Override
public String[] checksums() {
return delegateLauncher.checksums();
}
};
@Override
public Launcher launcher() {
return this._launcher;
}
@Override
public String version() {
return delegate.version();
}
@Override
public ClassLoader loader() {
return metaLoader.getParent();
}
@Override
public File[] jars() {
return delegate.jars();
}
@Override
@Deprecated()
public File libraryJar() {
return delegate.libraryJar();
}
@Override
@Deprecated()
public File compilerJar() {
return delegate.compilerJar();
}
@Override
public AppProvider app(xsbti.ApplicationID id) {
return delegate.app(id);
}
private ClassLoader loaderLibraryOnly() {
return metaLoader.getParent().getParent();
}
};
}
@Override
public ScalaProvider scalaProvider() {
return instance;
}
@Override
public xsbti.ApplicationID id() {
return appProvider.id();
}
@Override
public ClassLoader loader() {
return metaLoader;
}
@Override
@Deprecated()
public Class<? extends AppMain> mainClass() {
return appProvider.mainClass();
}
@Override
public Class<?> entryPoint() {
return appProvider.entryPoint();
}
@Override
public AppMain newMain() {
return appProvider.newMain();
}
@Override
public File[] mainClasspath() {
return appProvider.mainClasspath();
}
@Override
public ComponentProvider components() {
return appProvider.components();
}
}
@Override
public String[] arguments() {
return configuration.arguments();
}
@Override
public File baseDirectory() {
return configuration.baseDirectory();
}
@Override
public AppProvider provider() {
try {
return new ModifiedAppProvider(configuration.provider());
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
}

View File

@ -0,0 +1,40 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal
import java.util.concurrent.{ ExecutorService, Executors }
import sbt.plugins.{ CorePlugin, IvyPlugin, JvmPlugin }
private[internal] object ClassLoaderWarmup {
def warmup(): Unit = {
if (Runtime.getRuntime.availableProcessors > 1) {
val executorService: ExecutorService =
Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors - 1)
def submit[R](f: => R): Unit = {
executorService.submit(new Runnable {
override def run(): Unit = { f; () }
})
()
}
submit(Class.forName("sbt.internal.parser.SbtParserInit").getConstructor().newInstance())
submit(CorePlugin.projectSettings)
submit(IvyPlugin.projectSettings)
submit(JvmPlugin.projectSettings)
submit(() => {
try {
val clazz = Class.forName("scala.reflect.runtime.package$")
clazz.getMethod("universe").invoke(clazz.getField("MODULE$").get(null))
} catch {
case _: Exception =>
}
executorService.shutdown()
})
}
}
}

View File

@ -1,186 +0,0 @@
/*
* 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.lang.reflect.InvocationTargetException
import java.net.URL
import java.util.concurrent.{ ExecutorService, Executors }
import ClassLoaderClose.close
import sbt.plugins.{ CorePlugin, IvyPlugin, JvmPlugin }
import xsbti._
private[internal] object ClassLoaderWarmup {
def warmup(): Unit = {
if (Runtime.getRuntime.availableProcessors > 1) {
val executorService: ExecutorService =
Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors - 1)
def submit[R](f: => R): Unit = {
executorService.submit(new Runnable {
override def run(): Unit = { f; () }
})
()
}
submit(Class.forName("sbt.internal.parser.SbtParserInit").getConstructor().newInstance())
submit(CorePlugin.projectSettings)
submit(IvyPlugin.projectSettings)
submit(JvmPlugin.projectSettings)
submit(() => {
try {
val clazz = Class.forName("scala.reflect.runtime.package$")
clazz.getMethod("universe").invoke(clazz.getField("MODULE$").get(null))
} catch {
case _: Exception =>
}
executorService.shutdown()
})
}
}
}
/**
* Generates a new app configuration and invokes xMainImpl.run. For AppConfigurations generated
* by recent launchers, it is unnecessary to modify the original configuration, but configurations
* generated by older launchers need to be modified to place the test interface jar higher in
* the class hierarchy. The methods this object are implemented without using the scala library
* so that we can avoid loading any classes from the old scala provider. Verified as of
* sbt 1.3.0 that there are no references to the scala standard library in any of the methods
* in this file.
*/
private[sbt] class XMainConfiguration {
def run(moduleName: String, configuration: xsbti.AppConfiguration): xsbti.MainResult = {
val topLoader = configuration.provider.scalaProvider.launcher.topLoader
val updatedConfiguration =
try {
val method = topLoader.getClass.getMethod("getJLineJars")
val jars = method.invoke(topLoader).asInstanceOf[Array[URL]]
var canReuseConfiguration = jars.length == 3
var j = 0
while (j < jars.length && canReuseConfiguration) {
val s = jars(j).toString
canReuseConfiguration = s.contains("jline") || s.contains("jansi")
j += 1
}
if (canReuseConfiguration && j == 3) configuration else makeConfiguration(configuration)
} catch {
case _: NoSuchMethodException => makeConfiguration(configuration)
}
val loader = updatedConfiguration.provider.loader
Thread.currentThread.setContextClassLoader(loader)
val clazz = loader.loadClass(s"sbt.$moduleName$$")
val instance = clazz.getField("MODULE$").get(null)
val runMethod = clazz.getMethod("run", classOf[xsbti.AppConfiguration])
try {
val clw = loader.loadClass("sbt.internal.ClassLoaderWarmup$")
clw.getMethod("warmup").invoke(clw.getField("MODULE$").get(null))
runMethod.invoke(instance, updatedConfiguration).asInstanceOf[xsbti.MainResult]
} catch {
case e: InvocationTargetException =>
// This propogates xsbti.FullReload to the launcher
throw e.getCause
}
}
private def makeConfiguration(configuration: xsbti.AppConfiguration): xsbti.AppConfiguration = {
val baseLoader = classOf[XMainConfiguration].getClassLoader
val className = "sbt/internal/XMainConfiguration.class"
val url = baseLoader.getResource(className)
val path = url.toString.replaceAll(s"$className$$", "")
val urlArray = new Array[URL](1)
urlArray(0) = new URL(path)
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 XMainClassLoader(urlArray, topLoader)
val xMainConfigurationClass = modifiedLoader.loadClass("sbt.internal.XMainConfiguration")
val instance: AnyRef =
xMainConfigurationClass.getConstructor().newInstance().asInstanceOf[AnyRef]
val metaBuildLoaderClass = modifiedLoader.loadClass("sbt.internal.MetaBuildLoader")
val method = metaBuildLoaderClass.getMethod("makeLoader", classOf[AppProvider])
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
val providerClass = scalaProvider.getClass
val _ = try {
val method = providerClass.getMethod("loaderLibraryOnly")
close(method.invoke(scalaProvider).asInstanceOf[ClassLoader])
1
} catch { case _: NoSuchMethodException => 1 }
close(scalaProvider.loader)
close(configuration.provider.loader)
cons.newInstance(instance, configuration, loader).asInstanceOf[xsbti.AppConfiguration]
}
/*
* Replaces the AppProvider.loader method with a new loader that puts the sbt test interface
* jar ahead of the rest of the sbt classpath in the classloading hierarchy.
*/
private[sbt] class ModifiedConfiguration(
val configuration: xsbti.AppConfiguration,
val metaLoader: ClassLoader
) extends xsbti.AppConfiguration {
private class ModifiedAppProvider(val appProvider: AppProvider) extends AppProvider {
private val delegate = configuration.provider.scalaProvider
object ModifiedScalaProvider extends ScalaProvider {
override def launcher(): Launcher = new Launcher {
private val delegateLauncher = delegate.launcher
private val interfaceLoader = metaLoader.loadClass("sbt.testing.Framework").getClassLoader
override def getScala(version: String): ScalaProvider = getScala(version, "")
override def getScala(version: String, reason: String): ScalaProvider =
getScala(version, reason, "org.scala-lang")
override def getScala(version: String, reason: String, scalaOrg: String): ScalaProvider =
delegateLauncher.getScala(version, reason, scalaOrg)
override def app(id: xsbti.ApplicationID, version: String): AppProvider =
delegateLauncher.app(id, version)
override def topLoader(): ClassLoader = interfaceLoader
override def globalLock(): GlobalLock = delegateLauncher.globalLock()
override def bootDirectory(): File = delegateLauncher.bootDirectory()
override def ivyRepositories(): Array[xsbti.Repository] =
delegateLauncher.ivyRepositories()
override def appRepositories(): Array[xsbti.Repository] =
delegateLauncher.appRepositories()
override def isOverrideRepositories: Boolean = delegateLauncher.isOverrideRepositories
override def ivyHome(): File = delegateLauncher.ivyHome()
override def checksums(): Array[String] = delegateLauncher.checksums()
}
override def version(): String = delegate.version
override def loader(): ClassLoader = metaLoader.getParent
override def jars(): Array[File] = delegate.jars
@deprecated("Implements deprecated api", "1.3.0")
override def libraryJar(): File = delegate.libraryJar
@deprecated("Implements deprecated api", "1.3.0")
override def compilerJar(): File = delegate.compilerJar
override def app(id: xsbti.ApplicationID): AppProvider = delegate.app(id)
def loaderLibraryOnly(): ClassLoader = metaLoader.getParent.getParent
}
override def scalaProvider(): ModifiedScalaProvider.type = ModifiedScalaProvider
override def id(): xsbti.ApplicationID = appProvider.id()
override def loader(): ClassLoader = metaLoader
@deprecated("Implements deprecated api", "1.3.0")
override def mainClass(): Class[_ <: AppMain] = appProvider.mainClass()
override def entryPoint(): Class[_] = appProvider.entryPoint()
override def newMain(): AppMain = appProvider.newMain()
override def mainClasspath(): Array[File] = appProvider.mainClasspath()
override def components(): ComponentProvider = appProvider.components()
}
override def arguments(): Array[String] = configuration.arguments
override def baseDirectory(): File = configuration.baseDirectory
override def provider(): AppProvider = new ModifiedAppProvider(configuration.provider)
}
}

View File

@ -20,13 +20,13 @@ object IvyReport {
def fromReportXML(doc: Document): ModuleGraph = {
def edgesForModule(id: GraphModuleId, revision: NodeSeq): Seq[Edge] =
for {
caller revision \ "caller"
caller <- revision \ "caller"
callerModule = moduleIdFromElement(caller, caller.attribute("callerrev").get.text)
} yield (moduleIdFromElement(caller, caller.attribute("callerrev").get.text), id)
val moduleEdges: Seq[(Module, Seq[Edge])] = for {
mod doc \ "dependencies" \ "module"
revision mod \ "revision"
mod <- doc \ "dependencies" \ "module"
revision <- mod \ "revision"
rev = revision.attribute("name").get.text
moduleId = moduleIdFromElement(mod, rev)
module = Module(

View File

@ -20,7 +20,7 @@ object DOT {
labelRendering: HTMLLabelRendering
): String = {
val nodes = {
for (n graph.nodes) yield {
for (n <- graph.nodes) yield {
val style = if (n.isEvicted) EvictedStyle else ""
val label = nodeFormation(n.id.organization, n.id.name, n.id.version)
""" "%s"[%s style="%s"]""".format(
@ -56,7 +56,7 @@ object DOT {
.filterNot(e => originWasEvicted(e) || evictionTargetEdges(e)) ++ evictedByEdges
val edges = {
for (e filteredEdges) yield {
for (e <- filteredEdges) yield {
val extra =
if (graph.module(e._1).isEvicted)
s""" [label="Evicted By" style="$EvictedStyle"]"""

View File

@ -15,7 +15,7 @@ import scala.xml.XML
object GraphML {
def saveAsGraphML(graph: ModuleGraph, outputFile: String): Unit = {
val nodesXml =
for (n graph.nodes)
for (n <- graph.nodes)
yield <node id={n.id.idString}><data key="d0">
<y:ShapeNode>
<y:NodeLabel>{n.id.idString}</y:NodeLabel>
@ -23,7 +23,7 @@ object GraphML {
</data></node>
val edgesXml =
for (e graph.edges)
for (e <- graph.edges)
yield <edge source={e._1.idString} target={e._2.idString}/>
val xml =

View File

@ -1 +1 @@
sbt.version=1.4.4
sbt.version=1.4.6