2010-07-17 18:07:41 +02:00
|
|
|
/* sbt -- Simple Build Tool
|
|
|
|
|
* Copyright 2008, 2009, 2010 Mark Harrah
|
|
|
|
|
*/
|
|
|
|
|
package sbt
|
|
|
|
|
|
2010-07-19 18:38:42 +02:00
|
|
|
import Execute.NodeView
|
2010-07-17 18:07:41 +02:00
|
|
|
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)
|
2010-07-19 18:31:22 +02:00
|
|
|
Commands.applicable(state).flatMap( _.run(in) ).headOption.getOrElse {
|
2010-07-17 18:07:41 +02:00
|
|
|
System.err.println("Unknown command '" + command + "'")
|
|
|
|
|
state.fail
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
object Commands
|
|
|
|
|
{
|
|
|
|
|
def applicable(state: State): Stream[Apply] =
|
2010-07-19 18:31:22 +02:00
|
|
|
state.processors.toStream.flatMap(_.applies(state) )
|
2010-07-17 18:07:41 +02:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-07-19 18:38:42 +02:00
|
|
|
|
|
|
|
|
def act = Command { case s @ State(p: Tasked) =>
|
|
|
|
|
new Apply {
|
|
|
|
|
def help = p.help
|
|
|
|
|
def run = in => {
|
|
|
|
|
lazy val (checkCycles, maxThreads) = p match {
|
|
|
|
|
case c: TaskSetup => (c.checkCycles, c.maxThreads)
|
|
|
|
|
case _ => (false, Runtime.getRuntime.availableProcessors)
|
|
|
|
|
}
|
|
|
|
|
for(task <- p.task(in.name, s)) yield
|
|
|
|
|
processResult(runTask(task, checkCycles, maxThreads)(p.taskToNode), s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-07-17 18:07:41 +02:00
|
|
|
|
|
|
|
|
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"
|
2010-07-19 18:38:42 +02:00
|
|
|
/** The list of command names that may be used to terminate the program.*/
|
2010-07-17 18:07:41 +02:00
|
|
|
val TerminateActions: Seq[String] = Seq(Exit, Quit)
|
2010-07-19 18:38:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def runTask[Task[_] <: AnyRef](root: Task[State], checkCycles: Boolean, maxWorkers: Int)(implicit taskToNode: NodeView[Task]): Result[State] =
|
|
|
|
|
{
|
|
|
|
|
val (service, shutdown) = CompletionService[Task[_], Completed](maxWorkers)
|
|
|
|
|
|
|
|
|
|
val x = new Execute[Task](checkCycles)(taskToNode)
|
|
|
|
|
try { x.run(root)(service) } finally { shutdown() }
|
|
|
|
|
}
|
|
|
|
|
def processResult[State](result: Result[State], original: State): State =
|
|
|
|
|
result match
|
|
|
|
|
{
|
|
|
|
|
case Value(v) => v
|
|
|
|
|
case Inc(Incomplete(tpe, message, causes, directCause)) => // tpe: IValue = Error, message: Option[String] = None, causes: Seq[Incomplete] = Nil, directCause: Option[Throwable] = None)
|
|
|
|
|
println("Task did not complete successfully (TODO: error logging)")
|
|
|
|
|
original
|
|
|
|
|
}
|
2010-07-17 18:07:41 +02:00
|
|
|
}
|