Initial code for the loader.

This commit is contained in:
Mark Harrah 2009-08-21 08:12:43 -04:00
parent 108807a773
commit b716d33ba3
16 changed files with 802 additions and 0 deletions

30
launch/Boot.scala Normal file
View File

@ -0,0 +1,30 @@
/* sbt -- Simple Build Tool
* Copyright 2009 Mark Harrah
*/
package xsbt.boot
import BootConfiguration.SbtMainClass
import java.io.File
// The entry point to the launcher
object Boot
{
def main(args: Array[String])
{
CheckProxy()
try { (new Launch(new File("."), SbtMainClass)).boot(args) }
catch
{
case b: BootException => errorAndExit(b)
case e =>
e.printStackTrace
errorAndExit(e)
}
System.exit(0)
}
private def errorAndExit(e: Throwable)
{
System.out.println("Error during sbt execution: " + e.toString)
System.exit(1)
}
}

View File

@ -0,0 +1,86 @@
/* sbt -- Simple Build Tool
* Copyright 2009 Mark Harrah
*/
package xsbt.boot
// project/boot/ [BootDirectoryName]
// scala-<version>/ [baseDirectoryName]
// lib/ [ScalaDirectoryName]
// sbt-<version>/ [sbtDirectoryName]
//
// see also ProjectProperties for the set of constants that apply to the build.properties file in a project
private object BootConfiguration
{
val SbtMainClass = "xsbt.Main"
// these are the module identifiers to resolve/retrieve
val ScalaOrg = "org.scala-lang"
val SbtOrg = "sbt"
val CompilerModuleName = "scala-compiler"
val LibraryModuleName = "scala-library"
val SbtModuleName = "simple-build-tool"
/** The Ivy conflict manager to use for updating.*/
val ConflictManagerName = "strict"
/** The name of the local Ivy repository, which is used when compiling sbt from source.*/
val LocalIvyName = "local"
/** The pattern used for the local Ivy repository, which is used when compiling sbt from source.*/
val LocalPattern = "[organisation]/[module]/[revision]/[type]s/[artifact].[ext]"
/** The artifact pattern used for the local Ivy repository.*/
def LocalArtifactPattern = LocalPattern
/** The Ivy pattern used for the local Ivy repository.*/
def LocalIvyPattern = LocalPattern
/** The class name prefix used to hide the Scala classes used by this loader from sbt
* and the project definition*/
val ScalaPackage = "scala."
/** The class name prefix used to hide the Ivy classes used by this loader from sbt
* and the project definition*/
val IvyPackage = "org.apache.ivy."
/** The class name prefix used to hide the sbt launcher classes from sbt and the project definition.
* Note that access to xsbti.boot classes are allowed.*/
val SbtBootPackage = "xsbt.boot."
/** The loader will check that these classes can be loaded and will assume that their presence indicates
* sbt and its dependencies have been downloaded.*/
val TestLoadSbtClasses = "xsbt.Main" :: "org.apache.ivy.Ivy" :: Nil
/** 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.ScalaObject" :: "scala.tools.nsc.GenericRunnerCommand" :: Nil
val ProjectDirectoryName = "project"
val BootDirectoryName = "boot"
val BuildPropertiesName ="build.properties"
val ScalaHomeProperty = "scala.home"
val UpdateLogName = "update.log"
val DefaultIvyConfiguration = "default"
/** The base URL to use to resolve sbt for download. */
val sbtRootBase = "http://simple-build-tool.googlecode.com/svn/artifacts/"
/** The name of the directory within the boot directory to retrieve scala to. */
val ScalaDirectoryName = "lib"
/** The Ivy pattern to use for retrieving the scala compiler and library. It is relative to the directory
* containing all jars for the requested version of scala. */
val scalaRetrievePattern = ScalaDirectoryName + "/[artifact].[ext]"
/** The Ivy pattern to use for retrieving sbt and its dependencies. It is relative to the directory
* containing all jars for the requested version of scala. */
def sbtRetrievePattern(sbtVersion: String) = sbtDirectoryName(sbtVersion) + "/[conf]/[artifact]-[revision].[ext]"
/** The Ivy pattern to use for resolving sbt and its dependencies from the Google code project.*/
def sbtResolverPattern(scalaVersion: String) = sbtRootBase + "[revision]/[type]s/[artifact].[ext]"
/** The name of the directory to retrieve sbt and its dependencies to.*/
def sbtDirectoryName(sbtVersion: String) = SbtOrg + "-" + sbtVersion
/** The name of the directory in the boot directory to put all jars for the given version of scala in.*/
def baseDirectoryName(scalaVersion: String) = "scala-" + scalaVersion
}
private object ProxyProperties
{
val HttpProxyEnv = "http_proxy"
val HttpProxyUser = "http_proxy_user"
val HttpProxyPassword = "http_proxy_pass"
val ProxyHost = "http.proxyHost"
val ProxyPort = "http.proxyPort"
val ProxyUser = "http.proxyUser"
val ProxyPassword = "http.proxyPassword"
}

37
launch/CheckProxy.scala Normal file
View File

@ -0,0 +1,37 @@
/* sbt -- Simple Build Tool
* Copyright 2009 Mark Harrah
*/
package xsbt.boot
import java.net.{MalformedURLException, URL}
object CheckProxy
{
def apply()
{
import ProxyProperties._
val httpProxy = System.getenv(HttpProxyEnv)
if(isDefined(httpProxy) && !isPropertyDefined(ProxyHost) && !isPropertyDefined(ProxyPort))
{
try
{
val proxy = new URL(httpProxy)
setProperty(ProxyHost, proxy.getHost)
val port = proxy.getPort
if(port >= 0)
System.setProperty(ProxyPort, port.toString)
copyEnv(HttpProxyUser, ProxyUser)
copyEnv(HttpProxyPassword, ProxyPassword)
}
catch
{
case e: MalformedURLException =>
System.out.println("Warning: could not parse http_proxy setting: " + e.toString)
}
}
}
private def copyEnv(envKey: String, sysKey: String) { setProperty(sysKey, System.getenv(envKey)) }
private def setProperty(key: String, value: String) { if(value != null) System.setProperty(key, value) }
private def isPropertyDefined(k: String) = isDefined(System.getProperty(k))
private def isDefined(s: String) = s != null && !s.isEmpty
}

View File

@ -0,0 +1,20 @@
/* sbt -- Simple Build Tool
* Copyright 2009 Mark Harrah
*/
package xsbt.boot
import BootConfiguration.{IvyPackage, SbtBootPackage, ScalaPackage}
/** A custom class loader to ensure the main part of sbt doesn't load any Scala or
* Ivy classes from the jar containing the loader. */
private[boot] final class BootFilteredLoader extends ClassLoader with NotNull
{
@throws(classOf[ClassNotFoundException])
override final def loadClass(className: String, resolve: Boolean): Class[_] =
{
if(className.startsWith(ScalaPackage) || className.startsWith(IvyPackage) || className.startsWith(SbtBootPackage))
throw new ClassNotFoundException(className)
else
super.loadClass(className, resolve)
}
}

164
launch/Launch.scala Normal file
View File

@ -0,0 +1,164 @@
/* 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
// contains constants and paths
import BootConfiguration._
import UpdateTarget.{UpdateScala, UpdateSbt}
import xsbti.boot.{Exit => IExit, Launcher, MainResult, Reboot, SbtConfiguration, SbtMain}
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, scalaVersion, sbtVersion))(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

View File

@ -0,0 +1,147 @@
/* sbt -- Simple Build Tool
* Copyright 2009 Mark Harrah
*/
package xsbt.boot
/*
Project does not exist, create new project? [y/N/s] y
Name:
Organization []:
Version [1.0]:
Scala version [2.7.5]:
sbt version [0.7]:
*/
import java.io.File
/** Constants related to reading/writing the build.properties file in a project.
* See BootConfiguration for general constants used by the loader. */
private object ProjectProperties
{
/** The properties key for storing the name of the project.*/
val NameKey = "project.name"
/** The properties key for storing the organization of the project.*/
val OrganizationKey = "project.organization"
/** The properties key for storing the version of the project.*/
val VersionKey = "project.version"
/** The properties key for storing the version of Scala used with the project.*/
val ScalaVersionKey = "scala.version"
/** The properties key for storing the version of sbt used to build the project.*/
val SbtVersionKey = "sbt.version"
/** The properties key to communicate to the main component of sbt that the project
* should be initialized after being loaded, typically by creating a default directory structure.*/
val InitializeProjectKey = "project.initialize"
/** The properties key that configures the project to be flattened a bit for use by quick throwaway projects.*/
val ScratchKey = "project.scratch"
/** The label used when prompting for the name of the user's project.*/
val NameLabel = "Name"
/** The label used when prompting for the organization of the user's project.*/
val OrganizationLabel = "Organization"
/** The label used when prompting for the version of the user's project.*/
val VersionLabel = "Version"
/** The label used when prompting for the version of Scala to use for the user's project.*/
val ScalaVersionLabel = "Scala version"
/** The label used when prompting for the version of sbt to use for the user's project.*/
val SbtVersionLabel = "sbt version"
/** The default organization of the new user project when the user doesn't explicitly specify one when prompted.*/
val DefaultOrganization = ""
/** The default version of the new user project when the user doesn't explicitly specify a version when prompted.*/
val DefaultVersion = "1.0"
/** The default version of sbt when the user doesn't explicitly specify a version when prompted.*/
val DefaultSbtVersion = "0.7"
/** The default version of Scala when the user doesn't explicitly specify a version when prompted.*/
val DefaultScalaVersion = "2.7.5"
// sets up the project properties for a throwaway project (flattens src and lib to the root project directory)
def scratch(file: File)
{
withProperties(file) { properties =>
for( (key, _, default, _) <- propertyDefinitions(false))
properties(key) = default.getOrElse("scratch")
properties(ScratchKey) = true.toString
}
}
// returns (scala version, sbt version)
def apply(file: File, setInitializeProject: Boolean): (String, String) = applyImpl(file, setInitializeProject, Nil)
def forcePrompt(file: File, propertyKeys: String*) = applyImpl(file, false, propertyKeys)
private def applyImpl(file: File, setInitializeProject: Boolean, propertyKeys: Iterable[String]): (String, String) =
{
val organizationOptional = file.exists
withProperties(file) { properties =>
properties -= propertyKeys
prompt(properties, organizationOptional)
if(setInitializeProject)
properties(InitializeProjectKey) = true.toString
}
}
// (key, label, defaultValue, promptRequired)
private def propertyDefinitions(organizationOptional: Boolean) =
(NameKey, NameLabel, None, true) ::
(OrganizationKey, OrganizationLabel, Some(DefaultOrganization), !organizationOptional) ::
(VersionKey, VersionLabel, Some(DefaultVersion), true) ::
(ScalaVersionKey, ScalaVersionLabel, Some(DefaultScalaVersion), true) ::
(SbtVersionKey, SbtVersionLabel, Some(DefaultSbtVersion), true) ::
Nil
private def prompt(fill: ProjectProperties, organizationOptional: Boolean)
{
for( (key, label, default, promptRequired) <- propertyDefinitions(organizationOptional))
{
val value = fill(key)
if(value == null && promptRequired)
fill(key) = readLine(label, default)
}
}
private def withProperties(file: File)(f: ProjectProperties => Unit) =
{
val properties = new ProjectProperties(file)
f(properties)
properties.save
(properties(ScalaVersionKey), properties(SbtVersionKey))
}
private def readLine(label: String, default: Option[String]): String =
{
val prompt =
default match
{
case Some(d) => "%s [%s]: ".format(label, d)
case None => "%s: ".format(label)
}
SimpleReader.readLine(prompt) orElse default match
{
case Some(line) => line
case None => throw new BootException("Project not loaded: " + label + " not specified.")
}
}
}
import java.io.{FileInputStream, FileOutputStream}
import java.util.Properties
private class ProjectProperties(file: File) extends NotNull
{
private[this] var modified = false
private[this] val properties = new Properties
if(file.exists)
{
val in = new FileInputStream(file)
try { properties.load(in) } finally { in.close() }
}
def update(key: String, value: String)
{
modified = true
properties.setProperty(key, value)
}
def apply(key: String) = properties.getProperty(key)
def save()
{
if(modified)
{
file.getParentFile.mkdirs()
val out = new FileOutputStream(file)
try { properties.store(out, "Project Properties") } finally { out.close() }
modified = false
}
}
def -= (keys: Iterable[String]) { for(key <- keys) properties.remove(key) }
}

26
launch/SimpleReader.scala Normal file
View File

@ -0,0 +1,26 @@
/* sbt -- Simple Build Tool
* Copyright 2009 Mark Harrah
*/
package xsbt.boot
import jline.ConsoleReader
object SimpleReader extends NotNull
{
protected[this] val reader =
{
val cr = new ConsoleReader
cr.setBellEnabled(false)
cr
}
def readLine(prompt: String) =
reader.readLine(prompt) match
{
case null => None
case x =>
val trimmed = x.trim
if(trimmed.isEmpty)
None
else
Some(trimmed)
}
}

227
launch/Update.scala Normal file
View File

@ -0,0 +1,227 @@
/* sbt -- Simple Build Tool
* Copyright 2009 Mark Harrah
*/
package xsbt.boot
import java.io.{File, FileWriter, PrintWriter, Writer}
import org.apache.ivy.{core, plugins, util, Ivy}
import core.LogOptions
import core.cache.DefaultRepositoryCacheManager
import core.event.EventManager
import core.module.id.ModuleRevisionId
import core.module.descriptor.{Configuration, DefaultDependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor}
import core.report.ResolveReport
import core.resolve.{ResolveEngine, ResolveOptions}
import core.retrieve.{RetrieveEngine, RetrieveOptions}
import core.sort.SortEngine
import core.settings.IvySettings
import plugins.resolver.{ChainResolver, FileSystemResolver, IBiblioResolver, URLResolver}
import util.{DefaultMessageLogger, Message}
import BootConfiguration._
private object UpdateTarget extends Enumeration
{
val UpdateScala, UpdateSbt = Value
}
import UpdateTarget.{UpdateSbt, UpdateScala}
/** Ensures that the Scala and sbt jars exist for the given versions or else downloads them.*/
final class Update(bootDirectory: File, sbtVersion: String, scalaVersion: String)
{
private def logFile = new File(bootDirectory, UpdateLogName)
/** A Writer to use to write the full logging information to a file for debugging. **/
private lazy val logWriter =
{
bootDirectory.mkdirs
new PrintWriter(new FileWriter(logFile))
}
/** The main entry point of this class for use by the Update module. It runs Ivy */
def apply(target: UpdateTarget.Value)
{
Message.setDefaultLogger(new SbtIvyLogger(logWriter))
try { update(target) }
catch
{
case e: Exception =>
e.printStackTrace(logWriter)
log(e.toString)
println(" (see " + logFile + " for complete log)")
}
finally { logWriter.close() }
}
/** Runs update for the specified target (updates either the scala or sbt jars for building the project) */
private def update(target: UpdateTarget.Value)
{
val settings = new IvySettings
val ivy = Ivy.newInstance(settings)
ivy.pushContext()
try { update(target, settings) }
finally { ivy.popContext() }
}
private def update(target: UpdateTarget.Value, settings: IvySettings)
{
import Configuration.Visibility.PUBLIC
// the actual module id here is not that important
val moduleID = new DefaultModuleDescriptor(createID(SbtOrg, "boot", "1.0"), "release", null, false)
moduleID.setLastModified(System.currentTimeMillis)
moduleID.addConfiguration(new Configuration(DefaultIvyConfiguration, PUBLIC, "", Array(), true, null))
// add dependencies based on which target needs updating
target match
{
case UpdateScala =>
addDependency(moduleID, ScalaOrg, CompilerModuleName, scalaVersion)
addDependency(moduleID, ScalaOrg, LibraryModuleName, scalaVersion)
update(settings, moduleID, target)
case UpdateSbt =>
addDependency(moduleID, SbtOrg, SbtModuleName + "_" + scalaVersion, sbtVersion)
update(settings, moduleID, target)
}
}
/** Runs the resolve and retrieve for the given moduleID, which has had its dependencies added already. */
private def update(settings: IvySettings, moduleID: DefaultModuleDescriptor, target: UpdateTarget.Value)
{
val eventManager = new EventManager
addResolvers(settings, scalaVersion, target)
settings.setDefaultConflictManager(settings.getConflictManager(ConflictManagerName))
settings.setBaseDir(bootDirectory)
resolve(settings, eventManager, moduleID)
retrieve(settings, eventManager, moduleID, target)
}
private def createID(organization: String, name: String, revision: String) =
ModuleRevisionId.newInstance(organization, name, revision)
/** Adds the given dependency to the default configuration of 'moduleID'. */
private def addDependency(moduleID: DefaultModuleDescriptor, organization: String, name: String, revision: String)
{
val dep = new DefaultDependencyDescriptor(moduleID, createID(organization, name, revision), false, false, true)
dep.addDependencyConfiguration(DefaultIvyConfiguration, "default")
moduleID.addDependency(dep)
}
private def resolve(settings: IvySettings, eventManager: EventManager, module: ModuleDescriptor)
{
val resolveOptions = new ResolveOptions
// this reduces the substantial logging done by Ivy, including the progress dots when downloading artifacts
resolveOptions.setLog(LogOptions.LOG_DOWNLOAD_ONLY)
val resolveEngine = new ResolveEngine(settings, eventManager, new SortEngine(settings))
val resolveReport = resolveEngine.resolve(module, resolveOptions)
if(resolveReport.hasError)
{
logExceptions(resolveReport)
println(Set(resolveReport.getAllProblemMessages.toArray: _*).mkString(System.getProperty("line.separator")))
throw new BootException("Error retrieving required libraries")
}
}
/** Exceptions are logged to the update log file. */
private def logExceptions(report: ResolveReport)
{
for(unresolved <- report.getUnresolvedDependencies)
{
val problem = unresolved.getProblem
if(problem != null)
problem.printStackTrace(logWriter)
}
}
/** Retrieves resolved dependencies using the given target to determine the location to retrieve to. */
private def retrieve(settings: IvySettings, eventManager: EventManager, module: ModuleDescriptor, target: UpdateTarget.Value)
{
val retrieveOptions = new RetrieveOptions
val retrieveEngine = new RetrieveEngine(settings, eventManager)
val pattern =
target match
{
// see BuildConfiguration
case UpdateSbt => sbtRetrievePattern(sbtVersion)
case UpdateScala => scalaRetrievePattern
}
retrieveEngine.retrieve(module.getModuleRevisionId, pattern, retrieveOptions);
}
/** Add the scala tools repositories and a URL resolver to download sbt from the Google code project.*/
private def addResolvers(settings: IvySettings, scalaVersion: String, target: UpdateTarget.Value)
{
val newDefault = new ChainResolver
newDefault.setName("redefined-public")
newDefault.add(localResolver(settings.getDefaultIvyUserDir.getAbsolutePath))
newDefault.add(mavenLocal)
newDefault.add(mavenMainResolver)
target match
{
case UpdateSbt =>
newDefault.add(sbtResolver(scalaVersion))
case UpdateScala =>
newDefault.add(mavenResolver("Scala-Tools Maven2 Repository", "http://scala-tools.org/repo-releases"))
newDefault.add(mavenResolver("Scala-Tools Maven2 Snapshots Repository", "http://scala-tools.org/repo-snapshots"))
}
onDefaultRepositoryCacheManager(settings)(_.setUseOrigin(true))
settings.addResolver(newDefault)
settings.setDefaultResolver(newDefault.getName)
}
private def onDefaultRepositoryCacheManager(settings: IvySettings)(f: DefaultRepositoryCacheManager => Unit)
{
settings.getDefaultRepositoryCacheManager match
{
case manager: DefaultRepositoryCacheManager => f(manager)
case _ => ()
}
}
/** Uses the pattern defined in BuildConfiguration to download sbt from Google code.*/
private def sbtResolver(scalaVersion: String) =
{
val pattern = sbtResolverPattern(scalaVersion)
val resolver = new URLResolver
resolver.setName("Sbt Repository")
resolver.addIvyPattern(pattern)
resolver.addArtifactPattern(pattern)
resolver
}
private def mavenLocal = mavenResolver("Maven2 Local", "file://" + System.getProperty("user.home") + "/.m2/repository/")
/** Creates a maven-style resolver.*/
private def mavenResolver(name: String, root: String) =
{
val resolver = defaultMavenResolver(name)
resolver.setRoot(root)
resolver
}
/** Creates a resolver for Maven Central.*/
private def mavenMainResolver = defaultMavenResolver("Maven Central")
/** Creates a maven-style resolver with the default root.*/
private def defaultMavenResolver(name: String) =
{
val resolver = new IBiblioResolver
resolver.setName(name)
resolver.setM2compatible(true)
resolver
}
private def localResolver(ivyUserDirectory: String) =
{
val localIvyRoot = ivyUserDirectory + "/local"
val artifactPattern = localIvyRoot + "/" + LocalArtifactPattern
val ivyPattern = localIvyRoot + "/" + LocalIvyPattern
val resolver = new FileSystemResolver
resolver.setName(LocalIvyName)
resolver.addIvyPattern(ivyPattern)
resolver.addArtifactPattern(artifactPattern)
resolver
}
/** Logs the given message to a file and to the console. */
private def log(msg: String) =
{
try { logWriter.println(msg) }
catch { case e: Exception => System.err.println("Error writing to update log file: " + e.toString) }
println(msg)
}
}
/** A custom logger for Ivy to ignore the messages about not finding classes
* intentionally filtered using proguard. */
private final class SbtIvyLogger(logWriter: PrintWriter) extends DefaultMessageLogger(Message.MSG_INFO) with NotNull
{
private val ignorePrefix = "impossible to define"
override def log(msg: String, level: Int)
{
logWriter.println(msg)
if(level <= getLevel && msg != null && !msg.startsWith(ignorePrefix))
System.out.println(msg)
}
override def rawlog(msg: String, level: Int) { log(msg, level) }
}

View File

@ -0,0 +1,5 @@
package xsbti.boot;
public interface Exit extends MainResult
{
public int code();
}

View File

@ -0,0 +1,19 @@
package xsbti.boot;
import java.io.File;
public interface Launcher extends ScalaProvider
{
public static final int InterfaceVersion = 1;
public void boot(String[] args);
public MainResult checkAndLoad(String[] args);
public MainResult load(String[] args);
public MainResult run(ClassLoader sbtLoader, SbtConfiguration configuration);
public File ProjectDirectory();
public File BootDirectory();
public File PropertiesFile();
public Launcher launcher(File base, String mainClassName);
}

View File

@ -0,0 +1,4 @@
package xsbti.boot;
public interface MainResult {}

View File

@ -0,0 +1,5 @@
package xsbti.boot;
public interface Reboot extends MainResult
{
public String[] arguments();
}

View File

@ -0,0 +1,9 @@
package xsbti.boot;
public interface SbtConfiguration
{
public String[] arguments();
public String scalaVersion();
public String sbtVersion();
public Launcher launcher();
}

View File

@ -0,0 +1,6 @@
package xsbti.boot;
public interface SbtMain
{
public MainResult run(SbtConfiguration configuration);
}

View File

@ -0,0 +1,9 @@
package xsbti.boot;
import java.io.File;
public interface ScalaProvider
{
public ClassLoader getScalaLoader(String scalaVersion);
public File getScalaHome(String scalaVersion);
}

View File

@ -2,6 +2,9 @@ import sbt._
class XSbt(info: ProjectInfo) extends ParentProject(info)
{
val launchInterfaceSub = project(launchPath / "interface", "Launcher Interface", new InterfaceProject(_))
val launchSub = project(launchPath, "Launcher", new LaunchProject(_), launchInterfaceSub)
val commonDeps = project("common", "Dependencies", new CommonDependencies(_))
val interfaceSub = project("interface", "Interface", new InterfaceProject(_))
@ -19,9 +22,14 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
val cacheSub = project("cache", "Cache", new CacheProject(_), taskSub, ioSub)
val compilerSub = project(compilePath, "Compile", new Base(_), interfaceSub, ivySub, ioSub, compilerInterfaceSub)
def launchPath = path("launch")
def utilPath = path("util")
def compilePath = path("compile")
class LaunchProject(info: ProjectInfo) extends Base(info)
{
val ivy = "org.apache.ivy" % "ivy" % "2.0.0"
}
class CommonDependencies(info: ProjectInfo) extends DefaultProject(info)
{
val sc = "org.scala-tools.testing" % "scalacheck" % "1.5" % "test->default"