diff --git a/compile/discover/Discovered.scala b/compile/discover/Discovered.scala new file mode 100644 index 000000000..7d883edac --- /dev/null +++ b/compile/discover/Discovered.scala @@ -0,0 +1,11 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt +package inc + +final case class Discovered(baseClasses: Set[String], annotations: Set[String], hasMain: Boolean, isModule: Boolean) +object Discovered +{ + def empty = new Discovered(Set.empty, Set.empty, false, false) +} \ No newline at end of file diff --git a/compile/discover/Discovery.scala b/compile/discover/Discovery.scala index 6f77712be..2dca86d12 100644 --- a/compile/discover/Discovery.scala +++ b/compile/discover/Discovery.scala @@ -86,9 +86,4 @@ object Discovery } def isUnit(t: Type): Boolean = named(t, "scala.Unit") -} -final case class Discovered(baseClasses: Set[String], annotations: Set[String], hasMain: Boolean, isModule: Boolean) -object Discovered -{ - def empty = new Discovered(Set.empty, Set.empty, false, false) } \ No newline at end of file diff --git a/compile/inc/Incremental.scala b/compile/inc/Incremental.scala index 8f6b98fec..4124b1565 100644 --- a/compile/inc/Incremental.scala +++ b/compile/inc/Incremental.scala @@ -11,6 +11,7 @@ import java.io.File object Incremental { + def println(s: String) = () def compile(sources: Set[File], previous: Analysis, current: ReadStamps, externalAPI: String => Source, doCompile: Set[File] => Analysis)(implicit equivS: Equiv[Stamp]): Analysis = { val initialChanges = changedInitial(sources, previous.stamps, previous.apis, current, externalAPI) diff --git a/compile/interface/API.scala b/compile/interface/API.scala index ceecac514..2190389b9 100644 --- a/compile/interface/API.scala +++ b/compile/interface/API.scala @@ -206,6 +206,8 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend private def processType(t: Type): xsbti.api.Type = { + class TypeCompat { def dealias = t } // 2.7.7 compatibility: don't bother dealiasing + implicit def compat(t: Type): TypeCompat = new TypeCompat t.dealias match { case NoPrefix => Constants.emptyType diff --git a/launch/src/main/resources/sbt/sbt.boot.properties b/launch/src/main/resources/sbt/sbt.boot.properties index 851660002..79c7602df 100644 --- a/launch/src/main/resources/sbt/sbt.boot.properties +++ b/launch/src/main/resources/sbt/sbt.boot.properties @@ -1,5 +1,5 @@ [scala] - version: 2.7.7 + version: 2.8.0 #classifiers: sources, javadoc [app] @@ -33,8 +33,8 @@ project.name: quick=set(test), new=prompt(Name), fill=prompt(Name) project.organization: new=prompt(Organization) project.version: quick=set(1.0), new=prompt(Version)[1.0], fill=prompt(Version)[1.0] - build.scala.versions: quick=set(2.7.7), new=prompt(Scala version)[2.7.7], fill=prompt(Scala version)[2.7.7] - sbt.version: quick=set(0.7.4), new=prompt(sbt version)[0.7.4], fill=prompt(sbt version)[0.7.4] + build.scala.versions: quick=set(2.8.0), new=prompt(Scala version)[2.8.0], fill=prompt(Scala version)[2.8.0] + sbt.version: quick=set(0.9.0-SNAPSHOT), new=prompt(sbt version)[0.9.0-SNAPSHOT], fill=prompt(sbt version)[0.9.0-SNAPSHOT] project.scratch: quick=set(true) project.initialize: quick=set(true), new=set(true) diff --git a/main/AggressiveCompiler.scala b/main/AggressiveCompiler.scala index 1b745bba5..4efbc3100 100644 --- a/main/AggressiveCompiler.scala +++ b/main/AggressiveCompiler.scala @@ -4,6 +4,7 @@ package sbt import sbt.compile.{AnalyzingCompiler, JavaCompiler} + import sbt.build.AggressiveCompile import java.io.File import System.{currentTimeMillis => now} import Path._ diff --git a/main/Command.scala b/main/Command.scala new file mode 100644 index 000000000..c77d10a76 --- /dev/null +++ b/main/Command.scala @@ -0,0 +1,46 @@ +/* sbt -- Simple Build Tool + * Copyright 2009, 2010 Mark Harrah + */ +package sbt + +sealed trait Command +{ + def applies: PartialFunction[State, Apply] +} +trait Apply +{ + def help: Seq[(String,String)] + def run: PartialFunction[Input, State] +} +object Command +{ + def apply(f: PartialFunction[State, Apply]): Command = + new Command { def applies = f } + + def simple(name: String, help: (String, String)*)(f: State => State): Command = + apply { case s => Apply(help) { case in if in.line == name => f(s) }} +} +object Apply +{ + def apply(h: Seq[(String,String)])(r: PartialFunction[Input, State]): Apply = + new Apply { def help = h; def run = r } +} + +trait Logged +{ + def log: Logger +} +trait HistoryEnabled +{ + def historyPath: Option[Path] +} + +final case class Input(line: String) +{ + def name: String = error("TODO") + def arguments: String = error("TODO") +} + +object Next extends Enumeration { + val Reload, Fail, Done, Continue = Value +} \ No newline at end of file diff --git a/main/Main.scala b/main/Main.scala new file mode 100644 index 000000000..fe0a826d5 --- /dev/null +++ b/main/Main.scala @@ -0,0 +1,100 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package sbt + +import complete.HistoryCommands +import HistoryCommands.{Start => HistoryPrefix} +import sbt.build.{AggressiveCompile, Build, BuildException, Parse, ParseException} +import scala.annotation.tailrec + +/** This class is the entry point for sbt.*/ +class xMain extends xsbti.AppMain +{ + final def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = + { + import Commands._ + val initialCommands = Seq(help, history, exit, load) + val state = State( () )( configuration, initialCommands, Set.empty, None, configuration.arguments.map(_.trim).toList, Next.Continue ) + run(state) + } + + @tailrec final def run(state: State): xsbti.MainResult = + { + import Next._ + state.next match + { + case Continue => run(next(state)) + case Fail => Exit(1) + case Done => Exit(0) + case Reload => + val app = state.configuration.provider + new Reboot(app.scalaProvider.version, state.commands, app.id, state.configuration.baseDirectory) + } + } + def next(state: State): State = state.process(process) + def process(command: String, state: State): State = + { + val in = Input(command) + Commands.applicable(state).flatMap( _.run.lift(in) ).headOption.getOrElse { + System.err.println("Unknown command '" + command + "'") + state.fail + } + } + +} + + +object Commands +{ + def applicable(state: State): Stream[Apply] = + state.processors.toStream.flatMap(_.applies.lift(state) ) + + def help = Command.simple("help", ("help", "Displays this help message.")) { s => + val message = applicable(s).flatMap(_.help).map { case (a,b) => a + " : " + b }.mkString("\n") + System.out.println(message) + s + } + + def history = Command { case s @ State(p: HistoryEnabled with Logged) => + Apply(HistoryCommands.descriptions) { + case in if in.line startsWith("!") => + HistoryCommands(in.line.substring(HistoryPrefix.length).trim, p.historyPath, 500/*JLine.MaxHistorySize*/, p.log.error _) match + { + case Some(commands) => + commands.foreach(println) //better to print it than to log it + (commands ::: s).continue + case None => s.fail + } + } + } + + def helpExit = (TerminateActions.mkString(", "), "Terminates the build.") + + def exit = Command { case s => Apply(helpExit :: Nil) { + case Input(line) if TerminateActions contains line => + s.exit(true) + } + } + + def load = Command { case s => Apply(Nil) { + case Input(line) if line.startsWith("load") => + loadCommand(line, s.configuration) match + { + case Right(newValue) => + ExitHooks.runExitHooks(s.exitHooks.toSeq) + s.copy(project = newValue)(exitHooks = Set.empty) + case Left(e) => e.printStackTrace; System.err.println(e.toString); s.fail // TODO: log instead of print + } + } + } + + def loadCommand(line: String, configuration: xsbti.AppConfiguration): Either[Throwable, Any] = + try { Right( Build( Parse(line)(configuration.baseDirectory), configuration ) ) } + catch { case e @ (_: ParseException | _: BuildException | _: xsbti.CompileFailed) => Left(e) } + + val Exit = "exit" + val Quit = "quit" + /** The list of lowercase command names that may be used to terminate the program.*/ + val TerminateActions: Seq[String] = Seq(Exit, Quit) +} \ No newline at end of file diff --git a/sbt/src/main/scala/sbt/MainControl.scala b/main/MainControl.scala similarity index 64% rename from sbt/src/main/scala/sbt/MainControl.scala rename to main/MainControl.scala index bd74859eb..8c9d4e4b8 100644 --- a/sbt/src/main/scala/sbt/MainControl.scala +++ b/main/MainControl.scala @@ -9,7 +9,7 @@ private case class Exit(code: Int) extends xsbti.Exit { require(code >= 0) } -private class Reboot(val scalaVersion: String, argsList: List[String], val app: xsbti.ApplicationID, val baseDirectory: File) extends xsbti.Reboot +private class Reboot(val scalaVersion: String, argsList: Seq[String], val app: xsbti.ApplicationID, val baseDirectory: File) extends xsbti.Reboot { def arguments = argsList.toArray } @@ -24,8 +24,4 @@ private class ApplicationID(delegate: xsbti.ApplicationID, newVersion: String) e def crossVersioned = delegate.crossVersioned def classpathExtra = delegate.classpathExtra -} -private final class ReloadException(val remainingArguments: List[String], val buildScalaVersion: Option[String]) extends RuntimeException -{ - override def fillInStackTrace = this } \ No newline at end of file diff --git a/main/State.scala b/main/State.scala new file mode 100644 index 000000000..2cb7eb016 --- /dev/null +++ b/main/State.scala @@ -0,0 +1,46 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package sbt + +case class State(project: Any)( + val configuration: xsbti.AppConfiguration, + val processors: Seq[Command], + val exitHooks: Set[ExitHook], + val onFailure: Option[String], + val commands: Seq[String], + val next: Next.Value +) + +trait StateOps { + def process(f: (String, State) => State): State + def ::: (commands: Seq[String]): State + def :: (command: String): State + def continue: State + def reload: State + def exit(ok: Boolean): State + def fail: State +} +object State +{ + implicit def stateOps(s: State): StateOps = new StateOps { + def process(f: (String, State) => State): State = + s.commands match { + case x :: xs => f(x, s.copy()(commands = xs)) + case Nil => exit(true) + } + s.copy()(commands = s.commands.drop(1)) + def ::: (newCommands: Seq[String]): State = s.copy()(commands = newCommands ++ s.commands) + def :: (command: String): State = s.copy()(commands = command +: s.commands) + def setNext(n: Next.Value) = s.copy()(next = n) + def continue = setNext(Next.Continue) + def reload = setNext(Next.Reload) + def exit(ok: Boolean) = setNext(if(ok) Next.Fail else Next.Done) + def fail = + s.onFailure match + { + case Some(c) => s.copy()(commands = c :: Nil, onFailure = None) + case None => exit(ok = false) + } + } +} \ No newline at end of file diff --git a/main/AggressiveCompile.scala b/main/build/AggressiveCompile.scala similarity index 99% rename from main/AggressiveCompile.scala rename to main/build/AggressiveCompile.scala index fb5d541c7..4d8fbc19a 100644 --- a/main/AggressiveCompile.scala +++ b/main/build/AggressiveCompile.scala @@ -2,6 +2,7 @@ * Copyright 2010 Mark Harrah */ package sbt +package build import inc._ diff --git a/main/build/Build.scala b/main/build/Build.scala new file mode 100644 index 000000000..027c1bba4 --- /dev/null +++ b/main/build/Build.scala @@ -0,0 +1,98 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt +package build + +import java.io.File +import classpath.ClasspathUtilities.toLoader +import ModuleUtilities.getObject +import compile.{AnalyzingCompiler, JavaCompiler} +import Path._ + +final class BuildException(msg: String) extends RuntimeException(msg) + +object Build +{ + def loader(configuration: xsbti.AppConfiguration): ClassLoader = + configuration.provider.mainClass.getClassLoader + + def apply(command: LoadCommand, configuration: xsbti.AppConfiguration): Any = + command match + { + case BinaryLoad(classpath, module, name) => + binary(classpath, module, name, loader(configuration)) + case SourceLoad(classpath, sourcepath, output, module, auto, name) => + source(classpath, sourcepath, output, module, auto, name, configuration) + case _ => error("Not implemented yet") + } + + def binary(classpath: Seq[File], module: Boolean, name: String, parent: ClassLoader): Any = + { + if(name.isEmpty) + error("Class name required to load binary project.") + else + { + val loader = toLoader(classpath, parent) + if(module) + getObject(name, loader) + else + { + val clazz = Class.forName(name, true, loader) + clazz.newInstance + } + } + } + + def source(classpath: Seq[File], sources: Seq[File], output: Option[File], module: Boolean, auto: Auto.Value, name: String, configuration: xsbti.AppConfiguration): Any = + { + // TODO: accept Logger as an argument + val log = new ConsoleLogger with Logger with sbt.IvyLogger + + val scalaProvider = configuration.provider.scalaProvider + val launcher = scalaProvider.launcher + val instance = ScalaInstance(scalaProvider.version, launcher) + + val out = output.getOrElse(configuration.baseDirectory / "target" asFile) + val target = out / ("scala_" + instance.actualVersion) + val outputDirectory = target / "classes" + val cacheDirectory = target / "cache" + val projectClasspath = outputDirectory.asFile +: classpath + val compileClasspath = projectClasspath ++ configuration.provider.mainClasspath.toSeq + + val componentManager = new ComponentManager(launcher.globalLock, configuration.provider.components, log) + val compiler = new AnalyzingCompiler(instance, componentManager, log) + val javac = JavaCompiler.directOrFork(compiler.cp, compiler.scalaInstance)( (args: Seq[String], log: Logger) => Process("javac", args) ! log ) + + val agg = new AggressiveCompile(cacheDirectory) + val analysis = agg(compiler, javac, sources, compileClasspath, outputDirectory, Nil, Nil)(log) + + val discovered = discover(analysis, module, auto, name) + load(discovered)(x => binary(projectClasspath, module, x, loader(configuration)) ) + } + def discover(analysis: inc.Analysis, module: Boolean, auto: Auto.Value, name: String): Seq[String] = + { + import Auto.{Annotation, Explicit, Subclass} + auto match { + case Explicit => if(name.isEmpty) error("No name specified to load explicitly.") else Seq(name) + case Subclass => discover(analysis, module, new inc.Discovery(Set(name), Set.empty)) + case Annotation => discover(analysis, module, new inc.Discovery(Set.empty, Set(name))) + } + } + def discover(analysis: inc.Analysis, module: Boolean, discovery: inc.Discovery): Seq[String] = + { + for(src <- analysis.apis.internal.values.toSeq; + (df, found) <- discovery(src.definitions) if found.isModule == module) + yield + df.name + } + def load(discovered: Seq[String])(doLoad: String => Any): Any = + discovered match + { + case Seq() => error("No project found") + case Seq(x) => doLoad(x) + case xs => error("Multiple projects found: " + discovered.mkString(", ")) + } + + def error(msg: String) = throw new BuildException(msg) +} \ No newline at end of file diff --git a/main/build/LoadCommand.scala b/main/build/LoadCommand.scala new file mode 100644 index 000000000..f1824fb31 --- /dev/null +++ b/main/build/LoadCommand.scala @@ -0,0 +1,18 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt +package build + +import java.io.File + +sealed trait LoadCommand +final case class BinaryLoad(classpath: Seq[File], module: Boolean, name: String) extends LoadCommand +final case class SourceLoad(classpath: Seq[File], sourcepath: Seq[File], output: Option[File], module: Boolean, auto: Auto.Value, name: String) extends LoadCommand +final case class ProjectLoad(base: File, name: String) extends LoadCommand + +object Auto extends Enumeration +{ + val Subclass, Annotation, Explicit = Value +} + diff --git a/main/build/Parse.scala b/main/build/Parse.scala new file mode 100644 index 000000000..32199b0e7 --- /dev/null +++ b/main/build/Parse.scala @@ -0,0 +1,104 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt +package build + +import java.io.File + +final class ParseException(msg: String) extends RuntimeException(msg) + +/** Parses a load command. +* +* load ::= 'load' (binary | source | project) +* +* binary ::= classpath module name +* source ::= classpath '-src' paths ('-d' dir)? ('-auto' ('sub' | 'annot'))? module name +* project ::= ('-project' path)? name? +* +* name ::= '-name' nameString +* module ::= ('-module' ('true'|'false') )? +* classpath ::= '-cp' paths +* path ::= pathChar+ +* paths ::= path (pathSep path)* +*/ + +object Parse +{ + import File.{pathSeparatorChar => sep} + + def error(msg: String) = throw new ParseException(msg) + def apply(commandString: String)(implicit base: File): LoadCommand = + { + val tokens = commandString.split("""\s+""").toSeq + if(tokens.isEmpty) error("Empty command") + else if(tokens.head != "load") error("Not a load command") + else + { + val args = tokens.drop(1) + val srcs = sourcepath(args) + val nme = name(args) + + lazy val cp = classpath(args) + lazy val mod = module(args) + lazy val proj = project(args).getOrElse(base) + + if(!srcs.isEmpty) + SourceLoad(cp, srcs, output(args), mod, auto(args), nme) + else if(!cp.isEmpty) + BinaryLoad(cp, mod, nme) + else + ProjectLoad(proj, nme) + } + } + + def auto(args: Seq[String]): Auto.Value = + getArg(args, "auto") match { + case None => Auto.Explicit + case Some("sub") => Auto.Subclass + case Some("annot") => Auto.Annotation + case Some(x) => error("Illegal auto argument '" + x + "'") + } + + def module(args: Seq[String]): Boolean = + getArg(args, "module") match { + case None | Some("false") => false + case Some("true") => true + case Some(x) => error("Expected boolean, got '" + x + "'") + } + + def name(args: Seq[String]): String = + getArg(args, "name") getOrElse("") + + def output(args: Seq[String])(implicit base: File): Option[File] = + getArg(args, "d") map file(base) + + def project(args: Seq[String])(implicit base: File): Option[File] = + getArg(args, "project") map file(base) + + def pathArg(args: Seq[String], name: String)(implicit base: File): Seq[File] = + getArg(args, name).toSeq flatMap paths + + def classpath(args: Seq[String])(implicit base: File): Seq[File] = pathArg(args, "cp") + def sourcepath(args: Seq[String])(implicit base: File): Seq[File] = pathArg(args, "src") + + def getArg(args: Seq[String], name: String): Option[String] = + { + val opt = "-" + name + val found = args.dropWhile(_ != opt) + + if(found.isEmpty) + None + else + found.drop(1).headOption match + { + case x @ Some(arg) if !arg.startsWith("-") => x + case _ => error("No argument provided for -" + name) + } + } + + def paths(implicit base: File): String => Seq[File] = + _ split sep map file(base) + + def file(base: File) = (path: String) => Path.fromString(base, path).asFile +} \ No newline at end of file diff --git a/project/build.properties b/project/build.properties index 35519cc7d..e679b1265 100644 --- a/project/build.properties +++ b/project/build.properties @@ -2,4 +2,4 @@ project.organization=org.scala-tools.sbt project.name=xsbt sbt.version=0.7.4 project.version=0.9.0-SNAPSHOT -build.scala.versions=2.8.0.RC6 +build.scala.versions=2.8.0 diff --git a/project/build/XSbt.scala b/project/build/XSbt.scala index 93b08022e..2a159980b 100644 --- a/project/build/XSbt.scala +++ b/project/build/XSbt.scala @@ -40,11 +40,14 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths val compilerSub = project(compilePath, "Compile", new CompileProject(_), launchInterfaceSub, interfaceSub, ivySub, ioSub, classpathSub, compileInterfaceSub, logSub) - val altCompilerSub = baseProject("main", "Alternate Compiler Test", - classfileSub, compileIncrementalSub, compilerSub, ioSub, logSub, discoverySub, compilePersistSub, processSub) + val buildSub = baseProject("main" / "build", "Project Builder", + classfileSub, classpathSub, compilePersistSub, compilerSub, compileIncrementalSub, interfaceSub, ivySub, launchInterfaceSub, logSub, discoverySub, processSub) + + val altCompilerSub = project("main", "Alternate Compiler Test", (i: ProjectInfo) => new Base(i) { override def normalizedName = "sbt" }, // temporary + buildSub, compileIncrementalSub, compilerSub, completeSub, discoverySub, ioSub, logSub, processSub) /** following modules are not updated for 2.8 or 0.9 */ - val testSub = project("scripted", "Test", new TestProject(_), ioSub) + /*val testSub = project("scripted", "Test", new TestProject(_), ioSub) val trackingSub = baseProject(cachePath / "tracking", "Tracking", cacheSub) @@ -53,7 +56,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths val installerSub = project(sbtPath / "install", "Installer", new InstallerProject(_) {}, sbtSub) - lazy val dist = task { None } dependsOn(launchSub.proguard, sbtSub.publishLocal, installerSub.publishLocal) + lazy val dist = task { None } dependsOn(launchSub.proguard, sbtSub.publishLocal, installerSub.publishLocal)*/ def baseProject(path: Path, name: String, deps: Project*) = project(path, name, new Base(_), deps : _*) def testedBase(path: Path, name: String, deps: Project*) = project(path, name, new TestedBase(_), deps : _*) @@ -131,8 +134,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths trait TestDependencies extends Project { val sc = "org.scala-tools.testing" %% "scalacheck" % "1.7" % "test" - val sp = "org.scala-tools.testing" %% "specs" % "1.6.5-SNAPSHOT" % "test" - val snaps = ScalaToolsSnapshots + val sp = "org.scala-tools.testing" %% "specs" % "1.6.5" % "test" } class LogProject(info: ProjectInfo) extends Base(info) with TestDependencies { @@ -144,7 +146,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths { // these compilation options are useful for debugging caches and task composition //override def compileOptions = super.compileOptions ++ List(Unchecked,ExplainTypes, CompileOption("-Xlog-implicits")) - val sbinary = "org.scala-tools.sbinary" %% "sbinary" % "0.3.1-SNAPSHOT" + val sbinary = "org.scala-tools.sbinary" %% "sbinary" % "0.3.1" } class Base(info: ProjectInfo) extends DefaultProject(info) with ManagedBase with Component with Licensed { @@ -246,7 +248,8 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths // sub projects for each version of Scala to precompile against other than the one sbt is built against // each sub project here will add ~100k to the download - lazy val precompiled28 = precompiledSub("2.8.0.RC6") + //lazy val precompiled28 = precompiledSub("2.8.0") + lazy val precompiled27 = precompiledSub("2.7.7") def precompiledSub(v: String) = project(info.projectPath, "Precompiled " + v, new Precompiled(v)(_), cip.info.dependencies.toSeq : _* /*doesn't include subprojects of cip*/ ) @@ -261,6 +264,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths * subproject does not depend on any Scala subprojects, so mixing versions is not a problem. */ override def compileClasspath = cip.compileClasspath --- cip.mainUnmanagedClasspath +++ mainUnmanagedClasspath + override def compileOptions = Nil // these ensure that the classes compiled against other versions of Scala are not exported (for compilation/testing/...) override def projectClasspath(config: Configuration) = Path.emptyPathFinder } diff --git a/run/Console.scala b/run/Console.scala new file mode 100644 index 000000000..239a35bba --- /dev/null +++ b/run/Console.scala @@ -0,0 +1,20 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009 Mark Harrah + */ +/*package sbt + +import java.io.File +import compile.AnalyzingCompiler + +final class Console(compiler: AnalyzingCompiler) extends NotNull +{ + /** Starts an interactive scala interpreter session with the given classpath.*/ + def apply(classpath: Iterable[File], log: Logger): Option[String] = + apply(classpath, Nil, "", log) + + def apply(classpath: Iterable[File], options: Seq[String], initialCommands: String, log: Logger): Option[String] = + { + def console0 = compiler.console(Path.getFiles(classpath), options, initialCommands, log) + JLine.withJLine( Run.executeTrapExit(console0, log) ) + } +}*/ \ No newline at end of file diff --git a/util/control/ExitHook.scala b/util/control/ExitHook.scala index 00f7b0d66..1e491b095 100644 --- a/util/control/ExitHook.scala +++ b/util/control/ExitHook.scala @@ -12,21 +12,11 @@ trait ExitHook extends NotNull def runBeforeExiting(): Unit } -trait ExitHookRegistry +object ExitHooks { - def register(hook: ExitHook): Unit - def unregister(hook: ExitHook): Unit -} - - -class ExitHooks extends ExitHookRegistry -{ - private val exitHooks = new scala.collection.mutable.HashSet[ExitHook] - def register(hook: ExitHook) { exitHooks += hook } - def unregister(hook: ExitHook) { exitHooks -= hook } /** Calls each registered exit hook, trapping any exceptions so that each hook is given a chance to run. */ - def runExitHooks(debug: String => Unit): List[Throwable] = - exitHooks.toList.flatMap( hook => + def runExitHooks(exitHooks: Seq[ExitHook]): Seq[Throwable] = + exitHooks.flatMap( hook => ErrorHandling.wideConvert( hook.runBeforeExiting() ).left.toOption ) } \ No newline at end of file diff --git a/web/WebApp.scala b/web/WebApp.scala index 4aa13f08d..856c781dc 100644 --- a/web/WebApp.scala +++ b/web/WebApp.scala @@ -13,11 +13,8 @@ object JettyRunner val DefaultPort = 8080 val DefaultScanInterval = 3 } -//TODO: don't pass registry, just handle it in client -class JettyRunner(configuration: JettyConfiguration, registry: ExitHookRegistry) extends ExitHook +class JettyRunner(configuration: JettyConfiguration) extends ExitHook { - registry.register(this) - def name = "jetty-shutdown" def runBeforeExiting() { stop() } private var running: Option[Stoppable] = None