mirror of https://github.com/sbt/sbt.git
remove JLine from the launcher
It is no longer necessary for it to be loaded in a stable class loader and line reading in the launcher does not require anything more advanced than java.io.Console.readLine(String). Scala versions 2.8 and later use the version that goes through JAnsi and for that it is sufficient to have JNA in a stable loader.
This commit is contained in:
parent
54c08115f6
commit
8c4ebabe19
|
|
@ -3,12 +3,9 @@ Copyright 2008, 2009, 2010 Mark Harrah, David MacIver
|
|||
Licensed under BSD-style license (see LICENSE)
|
||||
|
||||
Classes from the Scala library are distributed with the launcher.
|
||||
Copyright 2002-2008 EPFL, Lausanne
|
||||
Copyright 2002-2013 EPFL, Lausanne
|
||||
Licensed under BSD-style license (see licenses/LICENSE_Scala)
|
||||
|
||||
JLine is distributed with the launcher.
|
||||
It is licensed under a BSD-style license (see licenses/LICENSE_JLine)
|
||||
|
||||
Classes from Apache Ivy, licensed under the Apache License, Version 2.0
|
||||
(see licenses/LICENSE_Apache) are distributed with the launcher.
|
||||
It requires the following notice:
|
||||
|
|
@ -32,4 +29,4 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,12 +15,14 @@ object Boot
|
|||
println("sbt launcher version " + Package.getPackage("xsbt.boot").getImplementationVersion)
|
||||
case _ =>
|
||||
System.clearProperty("scala.home") // avoid errors from mixing Scala versions in the same JVM
|
||||
System.setProperty("jline.shutdownhook", "false")
|
||||
CheckProxy()
|
||||
initJansi()
|
||||
setLogFormat()
|
||||
run(args)
|
||||
}
|
||||
}
|
||||
// this arrangement is because Scala 2.7.7 does not properly optimize away
|
||||
// this arrangement is because Scala does not always properly optimize away
|
||||
// the tail recursion in a catch statement
|
||||
final def run(args: Array[String]): Unit = runImpl(args) match {
|
||||
case Some(newArgs) => run(newArgs)
|
||||
|
|
@ -47,19 +49,21 @@ object Boot
|
|||
private def exit(code: Int): Nothing =
|
||||
System.exit(code).asInstanceOf[Nothing]
|
||||
|
||||
private[this] def setLogFormat() {
|
||||
if(System.getProperty("sbt.log.format") eq null)
|
||||
System.setProperty("sbt.log.format", "true")
|
||||
}
|
||||
private def initJansi() {
|
||||
try {
|
||||
val c = Class.forName("org.fusesource.jansi.AnsiConsole")
|
||||
c.getMethod("systemInstall").invoke(null)
|
||||
if (System.getProperty("sbt.log.format") eq null)
|
||||
System.setProperty("sbt.log.format", "true")
|
||||
} catch {
|
||||
case ignore: ClassNotFoundException =>
|
||||
/* The below code intentionally traps everything. It technically shouldn't trap the
|
||||
* non-StackOverflowError VirtualMachineErrors and AWTError would be weird, but this is PermGen
|
||||
* mitigation code that should not render sbt completely unusable if jansi initialization fails.
|
||||
* [From Mark Harrah, https://github.com/sbt/sbt/pull/633#issuecomment-11957578].
|
||||
*/
|
||||
/* The below code intentionally traps everything. It technically shouldn't trap the
|
||||
* non-StackOverflowError VirtualMachineErrors and AWTError would be weird, but this is PermGen
|
||||
* mitigation code that should not render sbt completely unusable if jansi initialization fails.
|
||||
* [From Mark Harrah, https://github.com/sbt/sbt/pull/633#issuecomment-11957578].
|
||||
*/
|
||||
case ex: Throwable => println("Jansi found on class path but initialization failed: " + ex)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,8 +42,6 @@ private object BootConfiguration
|
|||
/** The class name prefix used to hide the launcher classes from the application.
|
||||
* Note that access to xsbti classes are allowed.*/
|
||||
final val SbtBootPackage = "xsbt.boot."
|
||||
/** The prefix for JLine resources.*/
|
||||
final val JLinePackagePath = "jline/"
|
||||
/** The loader will check that these classes can be loaded and will assume that their presence indicates
|
||||
* the Scala compiler and library have been downloaded.*/
|
||||
val TestLoadScalaClasses = "scala.Option" :: "scala.tools.nsc.Global" :: Nil
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ object Initialize
|
|||
lazy val selectFill = (_: AppProperty).fill
|
||||
def create(file: File, promptCreate: String, enableQuick: Boolean, spec: List[AppProperty])
|
||||
{
|
||||
SimpleReader.readLine(promptCreate + " (y/N" + (if(enableQuick) "/s" else "") + ") ") match
|
||||
readLine(promptCreate + " (y/N" + (if(enableQuick) "/s" else "") + ") ") match
|
||||
{
|
||||
case None => declined("")
|
||||
case Some(line) =>
|
||||
|
|
@ -52,7 +52,7 @@ object Initialize
|
|||
case set: SetProperty => properties.setProperty(name, set.value)
|
||||
case prompt: PromptProperty =>
|
||||
def noValue = declined("No value provided for " + prompt.label)
|
||||
SimpleReader.readLine(prompt.label + prompt.default.toList.map(" [" + _ + "]").mkString + ": ") match
|
||||
readLine(prompt.label + prompt.default.toList.map(" [" + _ + "]").mkString + ": ") match
|
||||
{
|
||||
case None => noValue
|
||||
case Some(line) =>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
package xsbt.boot
|
||||
|
||||
import BootConfiguration.{FjbgPackage, IvyPackage, JLinePackagePath, SbtBootPackage, ScalaPackage}
|
||||
import BootConfiguration.{FjbgPackage, IvyPackage, SbtBootPackage, ScalaPackage}
|
||||
import scala.collection.immutable.Stream
|
||||
|
||||
/** A custom class loader to ensure the main part of sbt doesn't load any Scala or
|
||||
|
|
@ -13,17 +13,17 @@ private[boot] final class BootFilteredLoader(parent: ClassLoader) extends ClassL
|
|||
@throws(classOf[ClassNotFoundException])
|
||||
override final def loadClass(className: String, resolve: Boolean): Class[_] =
|
||||
{
|
||||
// note that we allow xsbti.* and jline.*
|
||||
// note that we allow xsbti.*
|
||||
if(className.startsWith(ScalaPackage) || className.startsWith(IvyPackage) || className.startsWith(SbtBootPackage) || className.startsWith(FjbgPackage))
|
||||
throw new ClassNotFoundException(className)
|
||||
else
|
||||
super.loadClass(className, resolve)
|
||||
}
|
||||
override def getResources(name: String) = if(includeResource(name)) super.getResources(name) else excludedLoader.getResources(name)
|
||||
override def getResource(name: String) = if(includeResource(name)) super.getResource(name) else excludedLoader.getResource(name)
|
||||
def includeResource(name: String) = name.startsWith(JLinePackagePath)
|
||||
override def getResources(name: String) = excludedLoader.getResources(name)
|
||||
override def getResource(name: String) = excludedLoader.getResource(name)
|
||||
|
||||
// the loader to use when a resource is excluded. This needs to be at least parent.getParent so that it skips parent. parent contains
|
||||
// resources included in the launcher, which need to be ignored. Now that launcher can be unrooted (not the application entry point),
|
||||
// resources included in the launcher, which need to be ignored. Now that the launcher can be unrooted (not the application entry point),
|
||||
// this needs to be the Java extension loader (the loader with getParent == null)
|
||||
private val excludedLoader = Loaders(parent.getParent).head
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ package xsbt.boot
|
|||
|
||||
object Pre
|
||||
{
|
||||
def readLine(prompt: String): Option[String] = {
|
||||
val c = System.console()
|
||||
if(c eq null) None else Option(c.readLine(prompt))
|
||||
}
|
||||
def trimLeading(line: String) =
|
||||
{
|
||||
def newStart(i: Int): Int = if(i >= line.length || !Character.isWhitespace(line.charAt(i))) i else newStart(i+1)
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package xsbt.boot
|
||||
|
||||
import jline.console.ConsoleReader
|
||||
abstract class JLine
|
||||
{
|
||||
protected[this] val reader: ConsoleReader
|
||||
def readLine(prompt: String) = JLine.withJLine { unsynchronizedReadLine(prompt) }
|
||||
private[this] def unsynchronizedReadLine(prompt: String) =
|
||||
reader.readLine(prompt) match
|
||||
{
|
||||
case null => None
|
||||
case x => Some(x.trim)
|
||||
}
|
||||
}
|
||||
private object JLine
|
||||
{
|
||||
def terminal = jline.TerminalFactory.get
|
||||
def createReader() =
|
||||
terminal.synchronized
|
||||
{
|
||||
val cr = new ConsoleReader
|
||||
terminal.setEchoEnabled(true)
|
||||
cr.setBellEnabled(false)
|
||||
cr
|
||||
}
|
||||
def withJLine[T](action: => T): T =
|
||||
{
|
||||
val t = terminal
|
||||
t.synchronized
|
||||
{
|
||||
t.setEchoEnabled(false)
|
||||
try { action }
|
||||
finally { t.setEchoEnabled(true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
object SimpleReader extends JLine
|
||||
{
|
||||
protected[this] val reader = JLine.createReader()
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ object LaunchProguard
|
|||
)
|
||||
|
||||
def specific(launchSub: Reference): Seq[Setting[_]] = inConfig(Proguard)(Seq(
|
||||
keepFullClasses ++= "xsbti.**" :: "jline.**" :: Nil,
|
||||
keepFullClasses ++= "xsbti.**" :: Nil,
|
||||
keepClasses ++= "org.apache.ivy.plugins.resolver.URLResolver" :: "org.apache.ivy.plugins.resolver.IBiblioResolver" :: Nil,
|
||||
artifactPath <<= (target, version) { (out,v) => out / ("sbt-launch-" + v + ".jar") },
|
||||
options <++= (dependencyClasspath in (launchSub, Compile), compile in (launchSub,Compile), streams) map { (cp, analysis, s) => mapJars(cp.files, analysis.relations.allBinaryDeps.toSeq, s.log) },
|
||||
|
|
@ -95,18 +95,15 @@ object LaunchProguard
|
|||
|
||||
def mapInJars(inJars: Seq[File], log: Logger): Seq[String] =
|
||||
{
|
||||
val (jlineJars, notJLine) = inJars partition isJarX("jline")
|
||||
val (ivyJars, notIvy) = notJLine partition isJarX("ivy")
|
||||
val (ivyJars, notIvy) = inJars partition isJarX("ivy")
|
||||
val (libraryJar, remaining) = notIvy partition isJarX("scala-library")
|
||||
val (compilerJar, otherJars) = remaining partition isJarX("scala-compiler")
|
||||
|
||||
log.debug("proguard configuration:")
|
||||
log.debug("\tJLline jar location: " + jlineJars.mkString(", "))
|
||||
log.debug("\tIvy jar location: " + ivyJars.mkString(", "))
|
||||
log.debug("\tOther jars:\n\t" + otherJars.mkString("\n\t"))
|
||||
|
||||
((withJar(ivyJars.toSeq, "Ivy") + excludeString(excludeIvyResources)) ::
|
||||
(withJar(jlineJars, "JLine") + jlineFilter ) ::
|
||||
(withJar(libraryJar, "Scala library") + libraryFilter) ::
|
||||
otherJars.map(jar => mkpath(jar) + generalFilter).toList) map { "-injars " + _ }
|
||||
}
|
||||
|
|
@ -125,7 +122,6 @@ object LaunchProguard
|
|||
Nil
|
||||
|
||||
private def libraryFilter = "(!META-INF/**,!*.properties,!scala/actors/**,!scala/util/parsing/*.class,**.class)"
|
||||
private def jlineFilter = "(!META-INF/**)"
|
||||
private def generalFilter = "(!META-INF/**,!*.properties)"
|
||||
|
||||
private def withJar[T](files: Seq[File], name: String) = mkpath(files.headOption getOrElse error(name + " not present") )
|
||||
|
|
|
|||
|
|
@ -62,9 +62,9 @@ object Sbt extends Build
|
|||
// Utilities related to reflection, managing Scala versions, and custom class loaders
|
||||
lazy val classpathSub = baseProject(utilPath / "classpath", "Classpath") dependsOn(launchInterfaceSub, interfaceSub, ioSub) settings(scalaCompiler)
|
||||
// Command line-related utilities.
|
||||
lazy val completeSub = testedBaseProject(utilPath / "complete", "Completion") dependsOn(collectionSub, controlSub, ioSub) settings(jline)
|
||||
lazy val completeSub = testedBaseProject(utilPath / "complete", "Completion") dependsOn(collectionSub, controlSub, ioSub) settings(jline : _*)
|
||||
// logging
|
||||
lazy val logSub = testedBaseProject(utilPath / "log", "Logging") dependsOn(interfaceSub, processSub) settings(libraryDependencies += jlineDep % "optional")
|
||||
lazy val logSub = testedBaseProject(utilPath / "log", "Logging") dependsOn(interfaceSub, processSub) settings(jline : _*)
|
||||
// Relation
|
||||
lazy val relationSub = testedBaseProject(utilPath / "relation", "Relation") dependsOn(interfaceSub, processSub)
|
||||
// class file reader and analyzer
|
||||
|
|
@ -155,7 +155,8 @@ object Sbt extends Build
|
|||
def scriptedTask: Initialize[InputTask[Unit]] = InputTask(scriptedSource(dir => (s: State) => scriptedParser(dir))) { result =>
|
||||
(proguard in Proguard, fullClasspath in scriptedSbtSub in Test, scalaInstance in scriptedSbtSub, publishAll, scriptedSource, result) map {
|
||||
(launcher, scriptedSbtClasspath, scriptedSbtInstance, _, sourcePath, args) =>
|
||||
val loader = classpath.ClasspathUtilities.toLoader(scriptedSbtClasspath.files, scriptedSbtInstance.loader)
|
||||
val noJLine = new classpath.FilteredLoader(scriptedSbtInstance.loader, "jline." :: Nil)
|
||||
val loader = classpath.ClasspathUtilities.toLoader(scriptedSbtClasspath.files, noJLine)
|
||||
val m = ModuleUtilities.getObject("sbt.test.ScriptedTests", loader)
|
||||
val r = m.getClass.getMethod("run", classOf[File], classOf[Boolean], classOf[Array[String]], classOf[File], classOf[Array[String]])
|
||||
val launcherVmOptions = Array("-XX:MaxPermSize=256M") // increased after a failure in scripted source-dependencies/macro
|
||||
|
|
@ -190,7 +191,7 @@ object Sbt extends Build
|
|||
Util.inAllProjects(projects filterNot Set(root, sbtSub, scriptedBaseSub, scriptedSbtSub, scriptedPluginSub) map { p => LocalProject(p.id) }, scoped)
|
||||
|
||||
def launchSettings =
|
||||
Seq(jline, ivy, crossPaths := false,
|
||||
Seq(ivy, crossPaths := false,
|
||||
compile in Test <<= compile in Test dependsOn(publishLocal in interfaceSub, publishLocal in testSamples, publishLocal in launchInterfaceSub)
|
||||
// mappings in (Compile, packageBin) <++= (mappings in (launchInterfaceSub, Compile, packageBin) ).identity
|
||||
) ++
|
||||
|
|
|
|||
|
|
@ -156,7 +156,8 @@ object Common
|
|||
{
|
||||
def lib(m: ModuleID) = libraryDependencies += m
|
||||
lazy val jlineDep = "jline" % "jline" % "2.10"
|
||||
lazy val jline = lib(jlineDep)
|
||||
lazy val jansiDep = "org.fusesource.jansi" % "jansi" % "1.9" // jline pom doesn't explicitly declare it?
|
||||
lazy val jline = Seq(lib(jlineDep), lib(jansiDep))
|
||||
lazy val ivy = lib("org.apache.ivy" % "ivy" % "2.3.0-rc1")
|
||||
lazy val httpclient = lib("commons-httpclient" % "commons-httpclient" % "3.1")
|
||||
lazy val jsch = lib("com.jcraft" % "jsch" % "0.1.46" intransitive() )
|
||||
|
|
|
|||
Loading…
Reference in New Issue