Permalink
Browse files

* cleaned up build

 * made Launcher usable outside of official jar
  • Loading branch information...
1 parent e1e60fe commit 54bc694081c10e8ec0c6f648b9a04db9304c5684 @harrah committed Mar 19, 2010
@@ -41,7 +41,7 @@ object WithCompiler
boot.LaunchTest.withLauncher { launch =>
FileUtilities.withTemporaryDirectory { componentDirectory =>
val manager = new ComponentManager(xsbt.boot.Locks, new boot.ComponentProvider(componentDirectory), log)
- val compiler = new AnalyzingCompiler(ScalaInstance(scalaVersion, launch), manager)
+ val compiler = new AnalyzingCompiler(ScalaInstance(scalaVersion, launch), manager, log)
compiler.newComponentCompiler(log).clearCache(ComponentCompiler.compilerInterfaceID)
define(manager, ComponentCompiler.compilerInterfaceSrcID, getResource("CompilerInterface.scala"), getClassResource(classOf[jline.Completor]))
define(manager, ComponentCompiler.xsbtiID, getClassResource(classOf[xsbti.AnalysisCallback]))
View
@@ -9,6 +9,9 @@ import java.util.Properties
object Initialize
{
+ lazy val selectCreate = (_: AppProperty).create
+ lazy val selectQuick = (_: AppProperty).quick
+ 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
@@ -17,16 +20,16 @@ object Initialize
case Some(line) =>
line.toLowerCase match
{
- case "y" | "yes" => process(file, spec, _.create)
- case "s" => process(file, spec, _.quick)
+ case "y" | "yes" => process(file, spec, selectCreate)
+ case "s" => process(file, spec, selectQuick)
case "n" | "no" | "" => declined("")
case x =>
System.out.println(" '" + x + "' not understood.")
create(file, promptCreate, enableQuick, spec)
}
}
}
- def fill(file: File, spec: List[AppProperty]): Unit = process(file, spec, _.fill)
+ def fill(file: File, spec: List[AppProperty]): Unit = process(file, spec, selectFill)
def process(file: File, appProperties: List[AppProperty], select: AppProperty => Option[PropertyInit])
{
val properties = new Properties
@@ -18,7 +18,21 @@ private[boot] final class BootFilteredLoader(parent: ClassLoader) extends ClassL
else
super.loadClass(className, resolve)
}
- override def getResources(name: String) = if(includeResource(name)) super.getResources(name) else parent.getParent.getResources(name)
- override def getResource(name: String) = if(includeResource(name)) super.getResource(name) else parent.getParent.getResource(name)
+ 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)
+ // 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),
+ // this needs to be the Java extension loader (the loader with getParent == null)
+ private val excludedLoader = Loaders(parent.getParent).head
+}
+
+object Loaders
+{
+ def apply(loader: ClassLoader): Stream[ClassLoader] =
+ {
+ def loaders(loader: ClassLoader, accum: Stream[ClassLoader]): Stream[ClassLoader] =
+ if(loader eq null) accum else loaders(loader.getParent, Stream.cons(loader, accum))
+ loaders(getClass.getClassLoader.getParent, Stream.empty)
+ }
}
View
@@ -39,14 +39,19 @@ class Find(config: LaunchConfiguration) extends NotNull
}
val baseDirectory = found.getOrElse(current)
System.setProperty("user.dir", baseDirectory.getAbsolutePath)
- (config.map(f => resolve(baseDirectory, f)), baseDirectory)
+ (ResolvePaths(config, baseDirectory), baseDirectory)
}
- def resolve(baseDirectory: File, f: File): File =
+ private def hasProject(f: File) = f.isDirectory && search.paths.forall(p => ResolvePaths(f, p).exists)
+ private def path(f: File, acc: List[File]): List[File] = if(f eq null) acc else path(f.getParentFile, f :: acc)
+}
+object ResolvePaths
+{
+ def apply(config: LaunchConfiguration, baseDirectory: File): LaunchConfiguration =
+ config.map(f => apply(baseDirectory, f))
+ def apply(baseDirectory: File, f: File): File =
{
assert(baseDirectory.isDirectory) // if base directory is not a directory, URI.resolve will not work properly
val uri = new URI(null, null, f.getPath, null)
new File(baseDirectory.toURI.resolve(uri))
}
- private def hasProject(f: File) = f.isDirectory && search.paths.forall(p => resolve(f, p).exists)
- private def path(f: File, acc: List[File]): List[File] = if(f eq null) acc else path(f.getParentFile, f :: acc)
}
View
@@ -10,41 +10,34 @@ import java.net.URL
object Launch
{
- //val start = System.currentTimeMillis
- def time(label: String) = ()//System.out.println(label + " : " + (System.currentTimeMillis - start) / 1000.0 + " s")
def apply(arguments: List[String]): Unit = apply( (new File("")).getAbsoluteFile , arguments )
def apply(currentDirectory: File, arguments: List[String]): Unit =
Configuration.find(arguments, currentDirectory) match { case (configLocation, newArguments) => configured(currentDirectory, configLocation, newArguments) }
def configured(currentDirectory: File, configLocation: URL, arguments: List[String]): Unit =
{
- time("found boot config")
val config = Configuration.parse(configLocation, currentDirectory)
- time("parsed")
Find(config, currentDirectory) match { case (resolved, baseDirectory) => parsed(baseDirectory, resolved, arguments) }
}
def parsed(currentDirectory: File, parsed: LaunchConfiguration, arguments: List[String]): Unit =
{
- time("found working directory")
val propertiesFile = parsed.boot.properties
import parsed.boot.{enableQuick, promptCreate, promptFill}
if(isNonEmpty(promptCreate) && !propertiesFile.exists)
Initialize.create(propertiesFile, promptCreate, enableQuick, parsed.appProperties)
else if(promptFill)
Initialize.fill(propertiesFile, parsed.appProperties)
- time("initialized")
initialized(currentDirectory, parsed, arguments)
}
def initialized(currentDirectory: File, parsed: LaunchConfiguration, arguments: List[String]): Unit =
{
val resolved = ResolveVersions(parsed)
- time("resolved")
explicit(currentDirectory, resolved, arguments)
}
def explicit(currentDirectory: File, explicit: LaunchConfiguration, arguments: List[String]): Unit =
- launch( run(new Launch(explicit.boot.directory, explicit.repositories, explicit.scalaClassifiers)) ) (
+ launch( run(Launcher(explicit)) ) (
new RunConfiguration(explicit.getScalaVersion, explicit.app.toID, currentDirectory, arguments) )
def run(launcher: xsbti.Launcher)(config: RunConfiguration): xsbti.MainResult =
@@ -54,12 +47,8 @@ object Launch
val appProvider: xsbti.AppProvider = scalaProvider.app(app)
val appConfig: xsbti.AppConfiguration = new AppConfiguration(toArray(arguments), workingDirectory, appProvider)
- time("pre-load")
val main = appProvider.newMain()
- time("loaded")
- val result = main.run(appConfig)
- time("ran")
- result
+ main.run(appConfig)
}
final def launch(run: RunConfiguration => xsbti.MainResult)(config: RunConfiguration)
{
@@ -130,6 +119,27 @@ class Launch(val bootDirectory: File, repositories: List[Repository], scalaClass
}
}
}
+object Launcher
+{
+ def apply(bootDirectory: File, repositories: List[Repository], scalaClassifiers: List[String]): xsbti.Launcher =
+ apply(bootDirectory, repositories, scalaClassifiers, GetLocks.find)
+ def apply(bootDirectory: File, repositories: List[Repository], scalaClassifiers: List[String], locks: xsbti.GlobalLock): xsbti.Launcher =
+ new Launch(bootDirectory, repositories, scalaClassifiers) {
+ override def globalLock = locks
+ }
+ def apply(explicit: LaunchConfiguration): xsbti.Launcher =
+ new Launch(explicit.boot.directory, explicit.repositories, explicit.scalaClassifiers)
+ def defaultAppProvider(baseDirectory: File): xsbti.AppProvider = getAppProvider(baseDirectory, Configuration.configurationOnClasspath)
+ def getAppProvider(baseDirectory: File, configLocation: URL): xsbti.AppProvider =
+ {
+ val parsed = ResolvePaths(Configuration.parse(configLocation, baseDirectory), baseDirectory)
+ Initialize.process(parsed.boot.properties, parsed.appProperties, Initialize.selectQuick)
+ val config = ResolveVersions(parsed)
+ val launcher = apply(config)
+ val scalaProvider = launcher.getScala(config.getScalaVersion)
+ scalaProvider.app(config.app.toID)
+ }
+}
class ComponentProvider(baseDirectory: File) extends xsbti.ComponentProvider
{
def componentLocation(id: String): File = new File(baseDirectory, id)
View
@@ -7,6 +7,18 @@ import java.io.{File, FileOutputStream}
import java.nio.channels.FileChannel
import java.util.concurrent.Callable
+object GetLocks
+{
+ /** Searches for Locks in parent class loaders before returning Locks from this class loader.
+ * Normal class loading doesn't work because the launcher class loader hides xsbt classes.*/
+ def find: xsbti.GlobalLock =
+ Loaders(getClass.getClassLoader.getParent).flatMap(tryGet).headOption.getOrElse(Locks)
+ private[this] def tryGet(loader: ClassLoader): List[xsbti.GlobalLock] =
+ try { getLocks0(loader) :: Nil } catch { case e: ClassNotFoundException => Nil }
+ private[this] def getLocks0(loader: ClassLoader) =
+ Class.forName("xsbt.boot.Locks$", true, loader).getField("MODULE$").get(null).asInstanceOf[xsbti.GlobalLock]
+}
+
// gets a file lock by first getting a JVM-wide lock.
object Locks extends xsbti.GlobalLock
{
@@ -6,8 +6,6 @@ import xsbti._
import org.specs._
import LaunchTest._
-final class Main // needed so that when we test Launch, it doesn't think sbt was improperly downloaded (it looks for xsbt.Main to verify the right jar was downloaded)
-
object ScalaProviderTest extends Specification
{
def provide = addToSusVerb("provide")
@@ -70,61 +68,21 @@ object LaunchTest
def testRepositories = List(Local, ScalaToolsReleases, ScalaToolsSnapshots).map(Repository.Predefined.apply)
def withLauncher[T](f: xsbti.Launcher => T): T =
FileUtilities.withTemporaryDirectory { bootDirectory =>
- f(new Launch(bootDirectory, testRepositories, Nil))
+ f(Launcher(bootDirectory, testRepositories, Nil))
}
def mapScalaVersion(versionNumber: String) = scalaVersionMap.find(_._2 == versionNumber).getOrElse {
error("Scala version number " + versionNumber + " from library.properties has no mapping")}._1
val scalaVersionMap = Map( ("2.7.2", "2.7.2") ) ++ List("2.7.3", "2.7.4", "2.7.5", "2.7.6", "2.7.7").map(v => (v, v + ".final"))
def getScalaVersion: String = getScalaVersion(getClass.getClassLoader)
- def getScalaVersion(loader: ClassLoader): String =
- {
- val propertiesStream = loader.getResourceAsStream("library.properties")
- val properties = new Properties
- properties.load(propertiesStream)
- properties.getProperty("version.number")
- }
- lazy val AppVersion =
+ def getScalaVersion(loader: ClassLoader): String = loadProperties(loader, "library.properties").getProperty("version.number")
+ lazy val AppVersion = loadProperties(getClass.getClassLoader, "xsbt.version.properties").getProperty("version")
+ private def getProperty(loader: ClassLoader, res: String, key: String) = loadProperties(loader, res).getProperty(key)
+ private def loadProperties(loader: ClassLoader, res: String): Properties =
{
val properties = new java.util.Properties
- println(getClass.getResource("/xsbt.version.properties"))
- val propertiesStream = getClass.getResourceAsStream("/xsbt.version.properties")
+ val propertiesStream = loader.getResourceAsStream(res)
try { properties.load(propertiesStream) } finally { propertiesStream.close() }
- "test-" + properties.getProperty("version")
- }
-}
-package test
-{
- class Exit(val code: Int) extends xsbti.Exit
- final class MainException(message: String) extends RuntimeException(message)
- final class ArgumentTest extends AppMain
- {
- def run(configuration: xsbti.AppConfiguration) =
- if(configuration.arguments.length == 0)
- throw new MainException("Arguments were empty")
- else
- new Exit(0)
+ properties
}
- class AppVersionTest extends AppMain
- {
- def run(configuration: xsbti.AppConfiguration) =
- {
- val expected = configuration.arguments.headOption.getOrElse("")
- if(configuration.provider.id.version == expected)
- new Exit(0)
- else
- throw new MainException("app version was " + configuration.provider.id.version + ", expected: " + expected)
- }
- }
- class ExtraTest extends AppMain
- {
- def run(configuration: xsbti.AppConfiguration) =
- {
- configuration.arguments.foreach { arg =>
- if(getClass.getClassLoader.getResource(arg) eq null)
- throw new MainException("Could not find '" + arg + "'")
- }
- new Exit(0)
- }
- }
-}
+}
@@ -0,0 +1,35 @@
+/** These are packaged and published locally and the resulting artifact is used to test the launcher.*/
+package xsbt.boot.test
+
+class Exit(val code: Int) extends xsbti.Exit
+final class MainException(message: String) extends RuntimeException(message)
+final class ArgumentTest extends xsbti.AppMain
+{
+ def run(configuration: xsbti.AppConfiguration) =
+ if(configuration.arguments.length == 0)
+ throw new MainException("Arguments were empty")
+ else
+ new Exit(0)
+}
+class AppVersionTest extends xsbti.AppMain
+{
+ def run(configuration: xsbti.AppConfiguration) =
+ {
+ val expected = configuration.arguments.headOption.getOrElse("")
+ if(configuration.provider.id.version == expected)
+ new Exit(0)
+ else
+ throw new MainException("app version was " + configuration.provider.id.version + ", expected: " + expected)
+ }
+}
+class ExtraTest extends xsbti.AppMain
+{
+ def run(configuration: xsbti.AppConfiguration) =
+ {
+ configuration.arguments.foreach { arg =>
+ if(getClass.getClassLoader.getResource(arg) eq null)
+ throw new MainException("Could not find '" + arg + "'")
+ }
+ new Exit(0)
+ }
+}
@@ -0,0 +1,34 @@
+import sbt._
+
+trait NoPublish extends ManagedBase
+{
+ override final def publishAction = task { None }
+ override final def deliverAction = publishAction
+}
+trait NoCrossPaths extends Project
+{
+ override def disableCrossPaths = true
+}
+trait JavaProject extends BasicScalaProject with NoCrossPaths
+{
+ // ensure that interfaces are only Java sources and that they cannot reference Scala classes
+ override def mainSources = descendents(mainSourceRoots, "*.java")
+ override def compileOrder = CompileOrder.JavaThenScala
+}
+trait SourceProject extends BasicScalaProject with NoCrossPaths
+{
+ override def packagePaths = mainResources +++ mainSources // the default artifact is a jar of the main sources and resources
+}
+trait ManagedBase extends BasicScalaProject
+{
+ override def deliverScalaDependencies = Nil
+ override def managedStyle = ManagedStyle.Ivy
+ override def useDefaultConfigurations = false
+ val defaultConf = Configurations.Default
+ val testConf = Configurations.Test
+}
+trait Component extends DefaultProject
+{
+ override def projectID = componentID match { case Some(id) => super.projectID extra("e:component" -> id); case None => super.projectID }
+ def componentID: Option[String] = None
+}
Oops, something went wrong.

0 comments on commit 54bc694

Please sign in to comment.