mirror of https://github.com/sbt/sbt.git
165 lines
6.1 KiB
Scala
165 lines
6.1 KiB
Scala
/* sbt -- Simple Build Tool
|
|
* Copyright 2009 Mark Harrah
|
|
*/
|
|
package xsbt.boot
|
|
|
|
// This is the main class for the sbt launcher. It reads the project/build.properties file to determine the
|
|
// version of Scala and sbt requested for the project definition. It downloads the requested version of
|
|
// Scala, sbt, and dependencies to the project's 'project/boot' directory. It loads the main sbt and then
|
|
// satisfies requests from the main sbt for different versions of Scala for use in the build.
|
|
|
|
import java.io.{File, FileFilter}
|
|
import java.net.URLClassLoader
|
|
|
|
import xsbti.boot.{Exit => IExit, Launcher, MainResult, Reboot, SbtConfiguration, SbtMain}
|
|
|
|
// contains constants and paths
|
|
import BootConfiguration._
|
|
import UpdateTarget.{UpdateScala, UpdateSbt}
|
|
|
|
|
|
class Launch(projectRootDirectory: File, mainClassName: String) extends Launcher with NotNull
|
|
{
|
|
import Launch._
|
|
final def boot(args: Array[String])
|
|
{
|
|
checkAndLoad(args) match
|
|
{
|
|
case e: Exit => System.exit(e.code)
|
|
case r: Reboot => boot(r.arguments())
|
|
}
|
|
}
|
|
def checkAndLoad(args: Array[String]): MainResult =
|
|
{
|
|
// prompt to create project if it doesn't exist.
|
|
checkProject() match
|
|
{
|
|
case Some(result) => result
|
|
case None => load(args)
|
|
}
|
|
}
|
|
/** Loads the project in the current working directory using the version of scala and sbt
|
|
* declared in the build. The class loader used prevents the Scala and Ivy classes used by
|
|
* this loader from being seen by the loaded sbt/project.*/
|
|
def load(args: Array[String]): MainResult =
|
|
{
|
|
val (definitionScalaVersion, useSbtVersion) = ProjectProperties.forcePrompt(PropertiesFile)//, forcePrompt : _*)
|
|
val scalaLoader = getScalaLoader(definitionScalaVersion)
|
|
val sbtLoader = createSbtLoader(useSbtVersion, definitionScalaVersion, scalaLoader)
|
|
val configuration = new SbtConfiguration
|
|
{
|
|
def arguments = args
|
|
def scalaVersion = definitionScalaVersion
|
|
def sbtVersion = useSbtVersion
|
|
def launcher: Launcher = Launch.this
|
|
}
|
|
run(sbtLoader, configuration)
|
|
}
|
|
def run(sbtLoader: ClassLoader, configuration: SbtConfiguration): MainResult =
|
|
{
|
|
val sbtMain = Class.forName(mainClassName, true, sbtLoader)
|
|
val main = sbtMain.newInstance.asInstanceOf[SbtMain]
|
|
main.run(configuration)
|
|
}
|
|
|
|
final val ProjectDirectory = new File(projectRootDirectory, ProjectDirectoryName)
|
|
final val BootDirectory = new File(ProjectDirectory, BootDirectoryName)
|
|
final val PropertiesFile = new File(ProjectDirectory, BuildPropertiesName)
|
|
|
|
final def checkProject() =
|
|
{
|
|
if(ProjectDirectory.exists)
|
|
None
|
|
else
|
|
{
|
|
val line = SimpleReader.readLine("Project does not exist, create new project? (y/N/s) : ")
|
|
if(isYes(line))
|
|
{
|
|
ProjectProperties(PropertiesFile, true)
|
|
None
|
|
}
|
|
else if(isScratch(line))
|
|
{
|
|
ProjectProperties.scratch(PropertiesFile)
|
|
None
|
|
}
|
|
else
|
|
Some(new Exit(1))
|
|
}
|
|
}
|
|
|
|
private val scalaLoaderCache = new scala.collection.jcl.WeakHashMap[String, ClassLoader]
|
|
def launcher(directory: File, mainClassName: String): Launcher = new Launch(directory, mainClassName)
|
|
def getScalaLoader(scalaVersion: String) = scalaLoaderCache.getOrElseUpdate(scalaVersion, createScalaLoader(scalaVersion))
|
|
def getScalaHome(scalaVersion: String) = new File(new File(BootDirectory, baseDirectoryName(scalaVersion)), ScalaDirectoryName)
|
|
|
|
def createScalaLoader(scalaVersion: String): ClassLoader =
|
|
{
|
|
val baseDirectory = new File(BootDirectory, baseDirectoryName(scalaVersion))
|
|
val scalaDirectory = new File(baseDirectory, ScalaDirectoryName)
|
|
val scalaLoader = newScalaLoader(scalaDirectory)
|
|
if(needsUpdate(scalaLoader, TestLoadScalaClasses))
|
|
{
|
|
(new Update(baseDirectory, "", scalaVersion))(UpdateScala)
|
|
val scalaLoader = newScalaLoader(scalaDirectory)
|
|
failIfMissing(scalaLoader, TestLoadScalaClasses, "Scala " + scalaVersion)
|
|
scalaLoader
|
|
}
|
|
else
|
|
scalaLoader
|
|
}
|
|
def createSbtLoader(sbtVersion: String, scalaVersion: String, scalaLoader: ClassLoader): ClassLoader =
|
|
{
|
|
val baseDirectory = new File(BootDirectory, baseDirectoryName(scalaVersion))
|
|
val sbtDirectory = new File(baseDirectory, sbtDirectoryName(sbtVersion))
|
|
|
|
val sbtLoader = newSbtLoader(sbtDirectory, scalaLoader)
|
|
if(needsUpdate(sbtLoader, TestLoadSbtClasses))
|
|
{
|
|
(new Update(baseDirectory, sbtVersion, scalaVersion))(UpdateSbt)
|
|
val sbtLoader = newSbtLoader(sbtDirectory, scalaLoader)
|
|
failIfMissing(sbtLoader, TestLoadSbtClasses, "sbt " + sbtVersion)
|
|
sbtLoader
|
|
}
|
|
else
|
|
sbtLoader
|
|
}
|
|
private def newScalaLoader(dir: File) = newLoader(dir, new BootFilteredLoader)
|
|
private def newSbtLoader(dir: File, scalaLoader: ClassLoader) = newLoader(dir, scalaLoader)
|
|
}
|
|
private object Launch
|
|
{
|
|
def isYes(so: Option[String]) = isValue("y", "yes")(so)
|
|
def isScratch(so: Option[String]) = isValue("s", "scratch")(so)
|
|
def isValue(values: String*)(so: Option[String]) =
|
|
so match
|
|
{
|
|
case Some(s) => values.contains(s.toLowerCase)
|
|
case None => false
|
|
}
|
|
private def failIfMissing(loader: ClassLoader, classes: Iterable[String], label: String) =
|
|
checkTarget(loader, classes, (), throw new BootException("Could not retrieve " + label + "."))
|
|
private def needsUpdate(loader: ClassLoader, classes: Iterable[String]) = checkTarget(loader, classes, false, true)
|
|
private def checkTarget[T](loader: ClassLoader, classes: Iterable[String], ifSuccess: => T, ifFailure: => T): T =
|
|
{
|
|
try
|
|
{
|
|
classes.foreach { c => Class.forName(c, false, loader) }
|
|
ifSuccess
|
|
}
|
|
catch { case e: ClassNotFoundException => ifFailure }
|
|
}
|
|
private def newLoader(directory: File, parent: ClassLoader) = new URLClassLoader(getJars(directory), parent)
|
|
private def getJars(directory: File) = wrapNull(directory.listFiles(JarFilter)).map(_.toURI.toURL)
|
|
private def wrapNull(a: Array[File]): Array[File] = if(a == null) Array() else a
|
|
}
|
|
private object JarFilter extends FileFilter
|
|
{
|
|
def accept(file: File) = !file.isDirectory && file.getName.endsWith(".jar")
|
|
}
|
|
final class Exit(val code: Int) extends IExit
|
|
|
|
// The exception to use when an error occurs at the launcher level (and not a nested exception).
|
|
// This indicates overrides toString because the exception class name is not needed to understand
|
|
// the error message.
|
|
private class BootException(override val toString: String) extends RuntimeException |