diff --git a/build.sbt b/build.sbt index 127a92dbb..943715364 100644 --- a/build.sbt +++ b/build.sbt @@ -130,20 +130,13 @@ lazy val runProj = (project in file("run")). utilLogging, (utilLogging % Test).classifier("tests"), compilerClasspath) ) -lazy val scriptedBaseProj = (project in scriptedPath / "base"). - settings( - testedBaseSettings, - name := "Scripted Framework", - libraryDependencies ++= scalaParsers.value ++ Seq(sbtIO) - ) - lazy val scriptedSbtProj = (project in scriptedPath / "sbt"). - dependsOn(scriptedBaseProj, commandProj). + dependsOn(commandProj). settings( baseSettings, name := "Scripted sbt", libraryDependencies ++= Seq(launcherInterface % "provided", - sbtIO, utilLogging, compilerInterface) + sbtIO, utilLogging, compilerInterface, utilScripted) ) lazy val scriptedPluginProj = (project in scriptedPath / "plugin"). @@ -236,7 +229,7 @@ lazy val myProvided = config("provided") intransitive def allProjects = Seq( testingProj, testAgentProj, taskProj, stdTaskProj, runProj, - scriptedBaseProj, scriptedSbtProj, scriptedPluginProj, + scriptedSbtProj, scriptedPluginProj, actionsProj, commandProj, mainSettingsProj, mainProj, sbtProj, bundledLauncherProj, mavenResolverPluginProj) def projectsWithMyProvided = allProjects.map(p => p.copy(configurations = (p.configurations.filter(_ != Provided)) :+ myProvided)) @@ -266,7 +259,7 @@ def otherRootSettings = Seq( } )) lazy val docProjects: ScopeFilter = ScopeFilter( - inAnyProject -- inProjects(sbtRoot, sbtProj, scriptedBaseProj, scriptedSbtProj, scriptedPluginProj, mavenResolverPluginProj), + inAnyProject -- inProjects(sbtRoot, sbtProj, scriptedSbtProj, scriptedPluginProj, mavenResolverPluginProj), inConfigurations(Compile) ) def fullDocSettings = Util.baseScalacOptions ++ Docs.settings ++ Sxr.settings ++ Seq( @@ -300,7 +293,7 @@ lazy val otherUnitTests = taskKey[Unit]("Unit test other projects") lazy val otherProjects: ScopeFilter = ScopeFilter( inProjects( testingProj, testAgentProj, taskProj, - scriptedBaseProj, scriptedSbtProj, scriptedPluginProj, + scriptedSbtProj, scriptedPluginProj, commandProj, mainSettingsProj, mainProj, sbtProj, mavenResolverPluginProj), inConfigurations(Test) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 066246b10..26d59b74d 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -9,7 +9,7 @@ object Dependencies { lazy val scala211 = "2.11.7" // sbt modules - val utilVersion = "0.1.0-M5" + val utilVersion = "0.1.0-M8" val ioVersion = "1.0.0-M3" val incremenalcompilerVersion = "0.1.0-M1-168cb7a4877917e01917e35b9b82a62afe5c2a01" val librarymanagementVersion = "0.1.0-M4" @@ -23,6 +23,7 @@ object Dependencies { lazy val utilRelation = "org.scala-sbt" %% "util-relation" % utilVersion lazy val utilLogic = "org.scala-sbt" %% "util-logic" % utilVersion lazy val utilTracking = "org.scala-sbt" %% "util-tracking" % utilVersion + lazy val utilScripted = "org.scala-sbt" %% "util-scripted" % utilVersion lazy val libraryManagement = "org.scala-sbt" %% "librarymanagement" % librarymanagementVersion lazy val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.0-M1" lazy val rawLauncher = "org.scala-sbt" % "launcher" % "1.0.0-M1" diff --git a/scripted/base/NOTICE b/scripted/base/NOTICE deleted file mode 100644 index e83f50bc7..000000000 --- a/scripted/base/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Scripted Test Component -Copyright 2009 Mark Harrah -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/scripted/base/src/main/scala/xsbt/test/CommentHandler.scala b/scripted/base/src/main/scala/xsbt/test/CommentHandler.scala deleted file mode 100644 index 09f51f2b4..000000000 --- a/scripted/base/src/main/scala/xsbt/test/CommentHandler.scala +++ /dev/null @@ -1,8 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.test - -object CommentHandler extends BasicStatementHandler { - def apply(command: String, args: List[String]) = () -} \ No newline at end of file diff --git a/scripted/base/src/main/scala/xsbt/test/FileCommands.scala b/scripted/base/src/main/scala/xsbt/test/FileCommands.scala deleted file mode 100644 index aef820626..000000000 --- a/scripted/base/src/main/scala/xsbt/test/FileCommands.scala +++ /dev/null @@ -1,123 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.test - -import scala.sys.process.Process -import java.io.File -import sbt.io.{ IO, Path } -import Path._ - -class FileCommands(baseDirectory: File) extends BasicStatementHandler { - lazy val commands = commandMap - def commandMap = - Map( - "touch" nonEmpty touch _, - "delete" nonEmpty delete _, - "exists" nonEmpty exists _, - "mkdir" nonEmpty makeDirectories _, - "absent" nonEmpty absent _, - // "sync" twoArg("Two directory paths", sync _), - "newer" twoArg ("Two paths", newer _), - "pause" noArg { println("Pausing in " + baseDirectory); readLine("Press enter to continue. "); println() }, - "sleep" oneArg ("Time in milliseconds", time => Thread.sleep(time.toLong)), - "exec" nonEmpty (execute _), - "copy" copy (to => rebase(baseDirectory, to)), - "copy-file" twoArg ("Two paths", copyFile _), - "must-mirror" twoArg ("Two paths", diffFiles _), - "copy-flat" copy flat - ) - - def apply(command: String, arguments: List[String]): Unit = - commands.get(command).map(_(arguments)).getOrElse(scriptError("Unknown command " + command)) - - def scriptError(message: String): Some[String] = sys.error("Test script error: " + message) - def spaced[T](l: Seq[T]) = l.mkString(" ") - def fromStrings(paths: List[String]) = paths.map(fromString) - def fromString(path: String) = new File(baseDirectory, path) - def touch(paths: List[String]) = IO.touch(fromStrings(paths)) - def delete(paths: List[String]): Unit = IO.delete(fromStrings(paths)) - /*def sync(from: String, to: String) = - IO.sync(fromString(from), fromString(to), log)*/ - def copyFile(from: String, to: String): Unit = - IO.copyFile(fromString(from), fromString(to)) - def makeDirectories(paths: List[String]) = - IO.createDirectories(fromStrings(paths)) - def diffFiles(file1: String, file2: String) = { - val lines1 = IO.readLines(fromString(file1)) - val lines2 = IO.readLines(fromString(file2)) - if (lines1 != lines2) - scriptError("File contents are different:\n" + lines1.mkString("\n") + "\nAnd:\n" + lines2.mkString("\n")) - } - - def newer(a: String, b: String) = - { - val pathA = fromString(a) - val pathB = fromString(b) - val isNewer = pathA.exists && (!pathB.exists || pathA.lastModified > pathB.lastModified) - if (!isNewer) { - scriptError(s"$pathA is not newer than $pathB") - } - } - def exists(paths: List[String]): Unit = { - val notPresent = fromStrings(paths).filter(!_.exists) - if (notPresent.nonEmpty) - scriptError("File(s) did not exist: " + notPresent.mkString("[ ", " , ", " ]")) - } - def absent(paths: List[String]): Unit = { - val present = fromStrings(paths).filter(_.exists) - if (present.nonEmpty) - scriptError("File(s) existed: " + present.mkString("[ ", " , ", " ]")) - } - def execute(command: List[String]): Unit = execute0(command.head, command.tail) - def execute0(command: String, args: List[String]): Unit = { - if (command.trim.isEmpty) - scriptError("Command was empty.") - else { - val exitValue = Process(command :: args, baseDirectory) !; - if (exitValue != 0) - sys.error("Nonzero exit value (" + exitValue + ")") - } - } - - // these are for readability of the command list - implicit def commandBuilder(s: String): CommandBuilder = new CommandBuilder(s) - final class CommandBuilder(commandName: String) extends NotNull { - type NamedCommand = (String, List[String] => Unit) - def nonEmpty(action: List[String] => Unit): NamedCommand = - commandName -> { paths => - if (paths.isEmpty) - scriptError("No arguments specified for " + commandName + " command.") - else - action(paths) - } - def twoArg(requiredArgs: String, action: (String, String) => Unit): NamedCommand = - commandName -> { - case List(from, to) => action(from, to) - case other => wrongArguments(requiredArgs, other) - } - def noArg(action: => Unit): NamedCommand = - commandName -> { - case Nil => action - case other => wrongArguments(other) - } - def oneArg(requiredArgs: String, action: String => Unit): NamedCommand = - commandName -> { - case List(single) => action(single) - case other => wrongArguments(requiredArgs, other) - } - def copy(mapper: File => FileMap): NamedCommand = - commandName -> { - case Nil => scriptError("No paths specified for " + commandName + " command.") - case path :: Nil => scriptError("No destination specified for " + commandName + " command.") - case paths => - val mapped = fromStrings(paths) - val map = mapper(mapped.last) - IO.copy(mapped.init pair map) - } - def wrongArguments(args: List[String]): Some[String] = - scriptError("Command '" + commandName + "' does not accept arguments (found '" + spaced(args) + "').") - def wrongArguments(requiredArgs: String, args: List[String]): Some[String] = - scriptError("Wrong number of arguments to " + commandName + " command. " + requiredArgs + " required, found: '" + spaced(args) + "'.") - } -} diff --git a/scripted/base/src/main/scala/xsbt/test/FilteredLoader.scala b/scripted/base/src/main/scala/xsbt/test/FilteredLoader.scala deleted file mode 100644 index 3466b34ad..000000000 --- a/scripted/base/src/main/scala/xsbt/test/FilteredLoader.scala +++ /dev/null @@ -1,17 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.test - -final class FilteredLoader(parent: ClassLoader) extends ClassLoader(parent) { - @throws(classOf[ClassNotFoundException]) - override final def loadClass(className: String, resolve: Boolean): Class[_] = - { - if (className.startsWith("java.") || className.startsWith("javax.")) - super.loadClass(className, resolve) - else - throw new ClassNotFoundException(className) - } - override def getResources(name: String) = null - override def getResource(name: String) = null -} \ No newline at end of file diff --git a/scripted/base/src/main/scala/xsbt/test/ScriptRunner.scala b/scripted/base/src/main/scala/xsbt/test/ScriptRunner.scala deleted file mode 100644 index f03ffcc44..000000000 --- a/scripted/base/src/main/scala/xsbt/test/ScriptRunner.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.test - -final class TestException(statement: Statement, msg: String, exception: Throwable) - extends RuntimeException(statement.linePrefix + " " + msg, exception) - -class ScriptRunner { - import scala.collection.mutable.HashMap - def apply(statements: List[(StatementHandler, Statement)]): Unit = { - val states = new HashMap[StatementHandler, Any] - def processStatement(handler: StatementHandler, statement: Statement): Unit = { - val state = states(handler).asInstanceOf[handler.State] - val nextState = - try { Right(handler(statement.command, statement.arguments, state)) } - catch { case e: Exception => Left(e) } - nextState match { - case Left(err) => - if (statement.successExpected) { - err match { - case t: TestFailed => throw new TestException(statement, "Command failed: " + t.getMessage, null) - case _ => throw new TestException(statement, "Command failed", err) - } - } else - () - case Right(s) => - if (statement.successExpected) - states(handler) = s - else - throw new TestException(statement, "Command succeeded but failure was expected", null) - } - } - val handlers = Set() ++ statements.map(_._1) - - try { - handlers.foreach { handler => states(handler) = handler.initialState } - statements foreach (Function.tupled(processStatement)) - } finally { - for (handler <- handlers; state <- states.get(handler)) { - try { handler.finish(state.asInstanceOf[handler.State]) } - catch { case e: Exception => () } - } - } - } -} diff --git a/scripted/base/src/main/scala/xsbt/test/StatementHandler.scala b/scripted/base/src/main/scala/xsbt/test/StatementHandler.scala deleted file mode 100644 index 25ed692be..000000000 --- a/scripted/base/src/main/scala/xsbt/test/StatementHandler.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.test - -trait StatementHandler { - type State - def initialState: State - def apply(command: String, arguments: List[String], state: State): State - def finish(state: State): Unit -} - -trait BasicStatementHandler extends StatementHandler { - final type State = Unit - final def initialState = () - final def apply(command: String, arguments: List[String], state: Unit): Unit = apply(command, arguments) - def apply(command: String, arguments: List[String]): Unit - def finish(state: Unit) = () -} - -/** Use when a stack trace is not useful */ -final class TestFailed(msg: String) extends RuntimeException(msg) { - override def fillInStackTrace = this -} \ No newline at end of file diff --git a/scripted/base/src/main/scala/xsbt/test/TestScriptParser.scala b/scripted/base/src/main/scala/xsbt/test/TestScriptParser.scala deleted file mode 100644 index 1c2d3b97d..000000000 --- a/scripted/base/src/main/scala/xsbt/test/TestScriptParser.scala +++ /dev/null @@ -1,81 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.test - -import java.io.{ BufferedReader, File, InputStreamReader } -import scala.util.parsing.combinator._ -import scala.util.parsing.input.Positional -import Character.isWhitespace -import sbt.io.IO - -/* -statement* -statement ::= startChar successChar word+ nl -startChar ::= -successChar ::= '+' | '-' -word ::= [^ \[\]]+ -comment ::= '#' \S* nl -nl ::= '\r' \'n' | '\n' | '\r' | eof -*/ -final case class Statement(command: String, arguments: List[String], successExpected: Boolean, line: Int) { - def linePrefix = "{line " + line + "} " -} - -private object TestScriptParser { - val SuccessLiteral = "success" - val FailureLiteral = "failure" - val WordRegex = """[^ \[\]\s'\"][^ \[\]\s]*""".r -} - -import TestScriptParser._ -class TestScriptParser(handlers: Map[Char, StatementHandler]) extends RegexParsers { - require(handlers.nonEmpty) - override def skipWhitespace = false - - import IO.read - if (handlers.keys.exists(isWhitespace)) - sys.error("Start characters cannot be whitespace") - if (handlers.keys.exists(key => key == '+' || key == '-')) - sys.error("Start characters cannot be '+' or '-'") - - def parse(scriptFile: File): List[(StatementHandler, Statement)] = parse(read(scriptFile), Some(scriptFile.getAbsolutePath)) - def parse(script: String): List[(StatementHandler, Statement)] = parse(script, None) - private def parse(script: String, label: Option[String]): List[(StatementHandler, Statement)] = - { - parseAll(statements, script) match { - case Success(result, next) => result - case err: NoSuccess => - { - val labelString = label.map("'" + _ + "' ").getOrElse("") - sys.error("Could not parse test script, " + labelString + err.toString) - } - } - } - - lazy val statements = rep1(space ~> statement <~ newline) - def statement: Parser[(StatementHandler, Statement)] = - { - trait PositionalStatement extends Positional { - def tuple: (StatementHandler, Statement) - } - positioned { - val command = (word | err("expected command")) - val arguments = rep(space ~> (word | failure("expected argument"))) - (successParser ~ (space ~> startCharacterParser <~ space) ~! command ~! arguments) ^^ - { - case successExpected ~ start ~ command ~ arguments => - new PositionalStatement { - def tuple = (handlers(start), new Statement(command, arguments, successExpected, pos.line)) - } - } - } ^^ (_.tuple) - } - def successParser: Parser[Boolean] = ('+' ^^^ true) | ('-' ^^^ false) | success(true) - def space: Parser[String] = """[ \t]*""".r - lazy val word: Parser[String] = ("\'" ~> "[^'\n\r]*".r <~ "\'") | ("\"" ~> "[^\"\n\r]*".r <~ "\"") | WordRegex - def startCharacterParser: Parser[Char] = elem("start character", handlers.contains _) | - ((newline | err("expected start character " + handlers.keys.mkString("(", "", ")"))) ~> failure("end of input")) - - def newline = """\s*([\n\r]|$)""".r -} diff --git a/scripted/base/src/test/scala/xsbt/test/FileCommandsSpec.scala b/scripted/base/src/test/scala/xsbt/test/FileCommandsSpec.scala deleted file mode 100644 index 34a329675..000000000 --- a/scripted/base/src/test/scala/xsbt/test/FileCommandsSpec.scala +++ /dev/null @@ -1,135 +0,0 @@ -package xsbt.test - -import java.io.File -import org.specs2.mutable.Specification -import org.specs2.matcher.FileMatchers -import sbt._ -import sbt.io.IO -import sbt.io.Path._ - -object FileCommandsSpec extends Specification with FileMatchers { - - "The touch command" should { - "touch a file that doesn't exist" in withTmpDir { dir => - fileCommands(dir)("touch", List("foo")) - dir / "foo" must exist - } - "update the timestamp of a file that does exist" in withTmpDir { dir => - val file = dir / "foo" - IO.write(file, "x") - file.setLastModified(1000L) - - fileCommands(dir)("touch", List("foo")) - file.lastModified() must beGreaterThan(1000L) - } - } - - "The delete command" should { - "delete a file" in withTmpDir { dir => - IO.write(dir / "foo", "x") - fileCommands(dir)("delete", List("foo")) - dir / "foo" must not(exist) - } - "delete a directory" in withTmpDir { dir => - IO.write(dir / "foo" / "bar", "x") - fileCommands(dir)("delete", List("foo")) - dir / "foo" must not(exist) - } - } - - "The exists command" should { - "succeed if a file exists" in withTmpDir { dir => - IO.write(dir / "foo", "x") - fileCommands(dir)("exists", List("foo")) - ok - } - "fail if a file doesn't exist" in withTmpDir { dir => - fileCommands(dir)("exists", List("foo")) must throwAn[Exception] - } - } - - "The mkdir command" should { - "make a directory" in withTmpDir { dir => - fileCommands(dir)("mkdir", List("foo")) - dir / "foo" must beADirectory - } - "make all directories" in withTmpDir { dir => - fileCommands(dir)("mkdir", List("foo/bar")) - dir / "foo" / "bar" must beADirectory - } - } - - "The absent command" should { - "succeed if a file is absent" in withTmpDir { dir => - fileCommands(dir)("absent", List("foo")) - ok - } - "fail if a file is not absent" in withTmpDir { dir => - IO.write(dir / "foo", "x") - fileCommands(dir)("absent", List("foo")) must throwAn[Exception] - } - } - - "The newer command" should { - "succeed if a file is newer" in withTmpDir { dir => - val file1 = dir / "foo" - IO.write(file1, "x") - file1.setLastModified(1000L) - val file2 = dir / "bar" - IO.write(file2, "x") - file2.setLastModified(2000L) - - fileCommands(dir)("newer", List("bar", "foo")) - ok - } - "fail if a file is not newer" in withTmpDir { dir => - val file1 = dir / "foo" - IO.write(file1, "x") - file1.setLastModified(1000L) - val file2 = dir / "bar" - IO.write(file2, "x") - file2.setLastModified(2000L) - - fileCommands(dir)("newer", List("foo", "bar")) must throwAn[Exception] - } - "fail if the tested file doesn't exist" in withTmpDir { dir => - val file1 = dir / "foo" - IO.write(file1, "x") - file1.setLastModified(1000L) - - fileCommands(dir)("newer", List("bar", "foo")) must throwAn[Exception] - } - "succeed if the target file doesn't exist" in withTmpDir { dir => - val file1 = dir / "foo" - IO.write(file1, "x") - file1.setLastModified(1000L) - - fileCommands(dir)("newer", List("foo", "bar")) - ok - } - } - - "The copy-file command" should { - "copy a file" in withTmpDir { dir => - IO.write(dir / "foo", "x") - fileCommands(dir)("copy-file", List("foo", "bar")) - dir / "bar" must exist - IO.read(dir / "bar") must_== "x" - - } - } - - def fileCommands(dir: File) = new FileCommands(dir) - - def withTmpDir[A](block: File => A): A = { - val tmpDir = File.createTempFile("filecommands", "") - try { - tmpDir.delete() - tmpDir.mkdir() - block(tmpDir) - } finally { - IO.delete(tmpDir) - } - - } -} diff --git a/scripted/sbt/src/main/scala/sbt/test/SbtHandler.scala b/scripted/sbt/src/main/scala/sbt/test/SbtHandler.scala index b11eea62a..4719b7ad2 100644 --- a/scripted/sbt/src/main/scala/sbt/test/SbtHandler.scala +++ b/scripted/sbt/src/main/scala/sbt/test/SbtHandler.scala @@ -6,7 +6,8 @@ package test import java.io.{ File, IOException } import xsbt.IPC -import xsbt.test.{ StatementHandler, TestFailed } + +import sbt.internal.scripted.{ StatementHandler, TestFailed } import sbt.util.Logger import sbt.util.Logger._ diff --git a/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala index b10ed94d5..26bd5dfb7 100644 --- a/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala @@ -9,7 +9,7 @@ import java.io.File import java.nio.charset.Charset import xsbt.IPC -import xsbt.test.{ CommentHandler, FileCommands, ScriptRunner, TestScriptParser } +import sbt.internal.scripted.{ CommentHandler, FileCommands, ScriptRunner, TestScriptParser } import sbt.io.{ DirectoryFilter, GlobFilter, HiddenFileFilter, Path } import sbt.io.IO.wrapNull import sbt.internal.io.Resources @@ -17,6 +17,8 @@ import sbt.internal.io.Resources import sbt.internal.util.{ BufferedLogger, ConsoleLogger, FullLogger } import sbt.util.{ AbstractLogger, Logger } +import sbt.internal.scripted.TestException + final class ScriptedTests(resourceBaseDirectory: File, bufferLog: Boolean, launcher: File, launchOpts: Seq[String]) { import ScriptedTests._ private val testResources = new Resources(resourceBaseDirectory) @@ -45,7 +47,7 @@ final class ScriptedTests(resourceBaseDirectory: File, bufferLog: Boolean, launc None } else { try { scriptedTest(str, testDirectory, prescripted, log); None } - catch { case _: xsbt.test.TestException | _: PendingTestSuccessException => Some(str) } + catch { case _: TestException | _: PendingTestSuccessException => Some(str) } } } } @@ -88,7 +90,7 @@ final class ScriptedTests(resourceBaseDirectory: File, bufferLog: Boolean, launc buffered.info("+ " + label + pendingString) if (pending) throw new PendingTestSuccessException(label) } catch { - case e: xsbt.test.TestException => + case e: TestException => testFailed() e.getCause match { case null | _: java.net.SocketException => buffered.error(" " + e.getMessage) @@ -147,7 +149,7 @@ class ScriptedRunner { run(resourceBaseDirectory, bufferLog, tests, logger, bootProperties, launchOpts, emptyCallback) def run(resourceBaseDirectory: File, bufferLog: Boolean, tests: Array[String], logger: AbstractLogger, bootProperties: File, - launchOpts: Array[String], prescripted: File => Unit) { + launchOpts: Array[String], prescripted: File => Unit): Unit = { val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, bootProperties, launchOpts) val allTests = get(tests, resourceBaseDirectory, logger) flatMap { case ScriptedTest(group, name) =>