diff --git a/project/build.properties b/project/build.properties index bdfab649a..88b6596f0 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1,7 +1,7 @@ #Project properties -#Thu Jun 25 20:59:30 EDT 2009 +#Sat Jul 04 12:43:34 EDT 2009 project.organization=sbt project.name=Simple Build Tool Parent -sbt.version=0.5 -project.version=0.5.2-SNAPSHOT +sbt.version=0.5.1 +project.version=0.5.2-p1 scala.version=2.7.2 diff --git a/project/build/src/CrossCompileProject.scala b/project/build/CrossCompileProject.scala similarity index 100% rename from project/build/src/CrossCompileProject.scala rename to project/build/CrossCompileProject.scala diff --git a/project/build/src/LoaderProject.scala b/project/build/LoaderProject.scala similarity index 100% rename from project/build/src/LoaderProject.scala rename to project/build/LoaderProject.scala diff --git a/project/build/src/SbtProject.scala b/project/build/SbtProject.scala similarity index 50% rename from project/build/src/SbtProject.scala rename to project/build/SbtProject.scala index ac06702b3..3c2df1980 100644 --- a/project/build/src/SbtProject.scala +++ b/project/build/SbtProject.scala @@ -18,13 +18,13 @@ class SbtProject(info: ProjectInfo) extends ParentProject(info) override def shouldCheckOutputDirectories = false override def baseUpdateOptions = QuietUpdate :: Nil - override def parallelExecution = true + //override def parallelExecution = true override def deliverLocalAction = noAction private def noAction = task { None } override def publishLocalAction = noAction } -protected class MainProject(val info: ProjectInfo) extends CrossCompileProject +protected class MainProject(val info: ProjectInfo) extends CrossCompileProject with test.ScalaScripted { override def mainSources = if(ScalaVersion.currentString.startsWith("2.8")) // cannot compile against test libraries currently @@ -37,38 +37,5 @@ protected class MainProject(val info: ProjectInfo) extends CrossCompileProject override def mainResources = super.mainResources +++ extraResources override def mainClass = Some("sbt.Main") override def testOptions = ExcludeTests("sbt.ReflectiveSpecification" :: "sbt.ProcessSpecification" :: Nil) :: super.testOptions.toList - - // ======== Scripted testing ========== - - def sbtTestResources = testResourcesPath / "sbt-test-resources" - - lazy val testNoScripted = super.testAction - override def testAction = testNoScripted dependsOn(scripted) - lazy val scripted = scriptedTask dependsOn(testCompile, `package`) - def scriptedTask = - task - { - log.info("Running scripted tests...") - log.info("") - // load ScriptedTests using a ClassLoader that loads from the project classpath so that the version - // of sbt being built is tested, not the one doing the building. - val loader = ScriptedLoader(scriptedClasspath.toArray) - val scriptedClass = Class.forName(ScriptedClassName, true, loader) - val scriptedConstructor = scriptedClass.getConstructor(classOf[File], classOf[Function2[String, String, Boolean]]) - val runner = scriptedConstructor.newInstance(sbtTestResources.asFile, filter) - runner.asInstanceOf[{def scriptedTests(log: Logger): Option[String]}].scriptedTests(log) - } - /** The classpath to use for scripted tests. This ensures that the version of sbt being built it the one used for testing.*/ - private def scriptedClasspath = - { - val buildClasspath = classOf[SbtProject]. getProtectionDomain.getCodeSource.getLocation.toURI.toURL - val scalacJar = FileUtilities.scalaCompilerJar.toURI.toURL - val ivy = runClasspath.get.filter(_.asFile.getName.startsWith("ivy-")).map(_.asURL).toList - val builtSbtJar = (outputPath / defaultJarName).asURL - builtSbtJar :: buildClasspath :: scalacJar :: ivy - } - - val ScriptedClassName = "scripted.ScriptedTests" - - val filter = (group: String, name: String) => true + override def scriptedDependencies = testCompile :: `package` :: Nil } \ No newline at end of file diff --git a/project/build/src/ScriptedLoader.scala b/project/build/src/ScriptedLoader.scala deleted file mode 100644 index cf73edb6c..000000000 --- a/project/build/src/ScriptedLoader.scala +++ /dev/null @@ -1,31 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -import sbt._ - -import java.net.URL - -package sbt { // need access to LoaderBase, which is private in package sbt - object ScriptedLoader - { - def apply(paths: Array[URL]): ClassLoader = new ScriptedLoader(paths) - } - private class ScriptedLoader(paths: Array[URL]) extends LoaderBase(paths, classOf[ScriptedLoader].getClassLoader) - { - private val delegateFor = List("sbt.Logger", "sbt.LogEvent", "sbt.SetLevel", "sbt.Success", "sbt.Log", "sbt.SetTrace", "sbt.Trace", "sbt.ControlEvent") - def doLoadClass(className: String): Class[_] = - { - // Logger needs to be loaded from the version of sbt building the project because we need to pass - // a Logger from that loader into ScriptedTests. - // All other sbt classes should be loaded from the project classpath so that we test those classes with 'scripted' - if(!shouldDelegate(className) && (className.startsWith("sbt.") || className.startsWith("scripted.") || className.startsWith("scala.tools."))) - findClass(className) - else - selfLoadClass(className) - } - - private def shouldDelegate(className: String) = delegateFor.exists(check => isNestedOrSelf(className, check)) - private def isNestedOrSelf(className: String, checkAgainst: String) = - className == checkAgainst || className.startsWith(checkAgainst + "$") - } -} \ No newline at end of file diff --git a/project/build/src/ScriptedTests.scala b/project/build/src/ScriptedTests.scala deleted file mode 100644 index 3d4650990..000000000 --- a/project/build/src/ScriptedTests.scala +++ /dev/null @@ -1,70 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ - -package scripted - -import sbt._ -import java.io.File - -trait ScriptedTestFilter extends NotNull -{ - def accept(group: String, name: String): Boolean -} -class BasicFilter(f: (String, String) => Boolean) extends ScriptedTestFilter -{ - def accept(group: String, name: String) = f(group, name) -} - -object AcceptAllFilter extends ScriptedTestFilter -{ - def accept(group: String, name: String): Boolean = true -} -class ScriptedTests(testResources: Resources, filter: ScriptedTestFilter) extends NotNull -{ - def this(resourceBaseDirectory: File, filter: (String, String) => Boolean) = this(new Resources(resourceBaseDirectory), new BasicFilter(filter)) - def this(resourceBaseDirectory: File, filter: ScriptedTestFilter) = this(new Resources(resourceBaseDirectory), filter) - def this(testResources: Resources) = this(testResources, AcceptAllFilter) - def this(resourceBaseDirectory: File) = this(new Resources(resourceBaseDirectory)) - - val ScriptFilename = "test" - import testResources._ - - private def includeDirectory(file: File) = file.getName != ".svn" - def scriptedTests(log: Logger): Option[String] = - { - System.setProperty("sbt.scala.version", "") - var success = true - for(group <- baseDirectory.listFiles(DirectoryFilter) if includeDirectory(group)) - { - log.info("Test group " + group.getName) - for(test <- group.listFiles(DirectoryFilter) if includeDirectory(test)) - { - val testName = test.getName - if(!filter.accept(group.getName, testName)) - log.warn(" Test " + testName + " skipped.") - else - scriptedTest(test, log) match - { - case Some(err) => - log.error(" Test " + testName + " failed: " + err) - success = false - case None => log.info(" Test " + testName + " succeeded.") - } - } - } - if(success) - None - else - Some("One or more tests failed.") - } - - def scriptedTest(group: String, name: String, log: Logger): Option[String] = - readOnlyResourceDirectory(group, name).fold(err => Some(err), testDirectory => scriptedTest(testDirectory, log)) - def scriptedTest(testDirectory: File, log: Logger): Option[String] = - { - (for(script <- (new TestScriptParser(testDirectory, log)).parse(new File(testDirectory, ScriptFilename)).right; - u <- withProject(testDirectory, log)(script).right ) - yield u).left.toOption - } -} diff --git a/project/build/src/TestScriptParser.scala b/project/build/src/TestScriptParser.scala deleted file mode 100644 index bab2fc1fc..000000000 --- a/project/build/src/TestScriptParser.scala +++ /dev/null @@ -1,269 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ - -package scripted - -import sbt._ -import java.io.{BufferedReader, File, InputStreamReader} - -/* -statement* -statement ::= ('$' | '>') word+ '[' word ']' -word ::= [^ \[\]]+ -comment ::= '#' [^ \n\r]* ('\n' | '\r' | eof) -*/ -import scala.util.parsing.combinator._ -import scala.util.parsing.input.Positional - -import TestScriptParser._ -private class TestScriptParser(baseDirectory: File, log: Logger) extends RegexParsers with NotNull -{ - type Statement = Project => Either[String, ReloadProject] - type PStatement = Statement with Positional - - private def evaluateList(list: List[PStatement])(p: Project): WithProjectResult[Unit] = - list match - { - case Nil => ValueResult(()) - case head :: tail => - head(p) match - { - case Left(msg) => new ErrorResult(msg) - case Right(reload) => ContinueResult(p =>evaluateList(tail)(p), reload) - } - } - - def script: Parser[Project => WithProjectResult[Unit]] = rep1(space ~> statement <~ space) ^^ evaluateList - def statement: Parser[PStatement] = - positioned - { - (StartRegex ~! rep1(word) ~! "[" ~! word ~! "]") ^^ - { - case start ~ command ~ open ~ result ~ close => - val successExpected = result.toLowerCase == SuccessLiteral.toLowerCase - new Statement with Positional - { selfPositional => - def apply(p: Project) = - { - val result = - try - { - start match - { - case CommandStart => evaluateCommand(command, successExpected, selfPositional)(p) - case ActionStart => evaluateAction(command, successExpected)(p).toLeft(NoReload) - } - } - catch - { - case e: Exception => - log.trace(e) - Left(e.toString) - } - result.left.map(message => linePrefix(this) + message) - } - } - } - } - private def linePrefix(p: Positional) = "{line " + p.pos.line + "} " - def space = """(\s+|(\#[^\n\r]*))*""".r - def word: Parser[String] = ("\'" ~> "[^'\n\r]*".r <~ "\'") | ("\"" ~> "[^\"\n\r]*".r <~ "\"") | WordRegex - def parse(scriptFile: File): Either[String, Project => WithProjectResult[Unit]] = - { - def parseReader(reader: java.io.Reader) = - parseAll(script, reader) match - { - case Success(result, next) => Right(result) - case err: NoSuccess => - { - val pos = err.next.pos - Left("Could not parse test script '" + scriptFile.getCanonicalPath + - "' (" + pos.line + "," + pos.column + "): " + err.msg) - } - } - FileUtilities.readValue(scriptFile, log)(parseReader) - } - - private def scriptError(message: String): Some[String] = Some("Test script error: " + message) - private def wrongArguments(commandName: String, args: List[String]): Some[String] = - scriptError("Command '" + commandName + "' does not accept arguments (found '" + spacedString(args) + "').") - private def wrongArguments(commandName: String, requiredArgs: String, args: List[String]): Some[String] = - scriptError("Wrong number of arguments to " + commandName + " command. " + requiredArgs + " required, found: '" + spacedString(args) + "'.") - private def evaluateCommand(command: List[String], successExpected: Boolean, position: Positional)(project: Project): Either[String, ReloadProject] = - { - command match - { - case "reload" :: Nil => Right(if(successExpected) new ReloadSuccessExpected(linePrefix(position)) else ReloadErrorExpected) - case x => evaluateCommandNoReload(x, successExpected)(project).toLeft(NoReload) - } - } - private def evaluateCommandNoReload(command: List[String], successExpected: Boolean)(project: Project): Option[String] = - { - evaluate(successExpected, "Command '" + command.firstOption.getOrElse("") + "'", project) - { - command match - { - case Nil => scriptError("No command specified.") - case "touch" :: paths => touch(paths, project) - case "delete" :: paths => delete(paths, project) - case "mkdir" :: paths => makeDirectories(paths, project) - case "copy-file" :: from :: to :: Nil => copyFile(from, to, project) - case "copy-file" :: args => wrongArguments("copy-file", "Two paths", args) - case "sync" :: from :: to :: Nil => sync(from, to, project) - case "sync" :: args => wrongArguments("sync", "Two directory paths", args) - case "copy" :: paths => copy(paths, project) - case "exists" :: paths => exists(paths, project) - case "absent" :: paths => absent(paths, project) - case "pause" :: Nil => readLine("Press enter to continue. "); println(); None - case "pause" :: args => wrongArguments("pause", args) - case "newer" :: a :: b :: Nil => newer(a, b, project) - case "newer" :: args => wrongArguments("newer", "Two paths", args) - case "sleep" :: time :: Nil => trap("Error while sleeping:") { Thread.sleep(time.toLong) } - case "sleep" :: args => wrongArguments("sleep", "Time in milliseconds", args) - case "exec" :: command :: args => execute(command, args, project) - case "exec" :: other => wrongArguments("exec", "Command and arguments", other) - case "reload" :: args => wrongArguments("reload", args) - case unknown :: arguments => scriptError("Unknown command " + unknown) - } - } - } - private def foreachBufferedLogger(project: Project)(f: BufferedLogger => Unit) - { - project.topologicalSort.foreach(p => p.log match { case buffered: BufferedLogger => f(buffered); case _ => () }) - } - private def evaluate(successExpected: Boolean, label: String, project: Project)(body: => Option[String]): Option[String] = - { - def startRecordingLog() { foreachBufferedLogger(project)(_.startRecording()) } - def playLog() { foreachBufferedLogger(project)(_.playAll()) } - def stopLog() { foreachBufferedLogger(project)(_.stop()) } - - startRecordingLog() - val result = - body match - { - case None => - if(successExpected) None - else - { - playLog() - Some(label + " succeeded (expected failure).") - } - case Some(failure) => - if(successExpected) - { - playLog() - Some(label + " failed (expected success): " + failure) - } - else None - } - stopLog() - result - } - private def evaluateAction(action: List[String], successExpected: Boolean)(project: Project): Option[String] = - { - def actionToString = action.mkString(" ") - action match - { - case Nil => scriptError("No action specified.") - case head :: Nil if project.taskNames.toSeq.contains(head)=> - evaluate(successExpected, "Action '" + actionToString + "'", project)(project.act(head)) - case head :: tail => - evaluate(successExpected, "Method '" + actionToString + "'", project)(project.call(head, tail.toArray)) - } - } - private def spacedString[T](l: Seq[T]) = l.mkString(" ") - private def wrap(result: Option[String]) = result.flatMap(scriptError) - private def trap(errorPrefix: String)(action: => Unit) = wrap( Control.trapUnit(errorPrefix, log) { action; None } ) - - private def fromStrings(paths: List[String], project: Project) = paths.map(path => fromString(path, project)) - private def fromString(path: String, project: Project) = Path.fromString(project.info.projectPath, path) - private def touch(paths: List[String], project: Project) = - if(paths.isEmpty) - scriptError("No paths specified for touch command.") - else - wrap(lazyFold(paths) { path => FileUtilities.touch(fromString(path, project), log) }) - - private def delete(paths: List[String], project: Project) = - if(paths.isEmpty) - scriptError("No paths specified for delete command.") - else - wrap(FileUtilities.clean(fromStrings(paths, project), true, log)) - private def sync(from: String, to: String, project: Project) = - wrap(FileUtilities.sync(fromString(from, project), fromString(to, project), log)) - private def copyFile(from: String, to: String, project: Project) = - wrap(FileUtilities.copyFile(fromString(from, project), fromString(to, project), log)) - private def copy(paths: List[String], project: Project) = - paths match - { - case Nil => scriptError("No paths specified for copy command.") - case path :: Nil => scriptError("No destination specified for copy command.") - case _ => - val mapped = fromStrings(paths, project).toArray - val last = mapped.length - 1 - wrap(FileUtilities.copy(mapped.take(last), mapped(last), log).left.toOption) - } - private def makeDirectories(paths: List[String], project: Project) = - fromStrings(paths, project) match - { - case Nil => scriptError("No paths specified for mkdir command.") - case p => FileUtilities.createDirectories(p, project.log) - } - private def newer(a: String, b: String, project: Project) = - trap("Error testing if '" + a + "' is newer than '" + b + "'") - { - val pathA = fromString(a, project) - val pathB = fromString(b, project) - pathA.exists && (!pathB.exists || pathA.lastModified > pathB.lastModified) - } - private def exists(paths: List[String], project: Project) = - fromStrings(paths, project).filter(!_.exists) match - { - case Nil => None - case x => Some("File(s) did not exist: " + x.mkString("[ ", " , ", " ]")) - } - private def absent(paths: List[String], project: Project) = - fromStrings(paths, project).filter(_.exists) match - { - case Nil => None - case x => Some("File(s) existed: " + x.mkString("[ ", " , ", " ]")) - } - private def execute(command: String, args: List[String], project: Project) = - { - if(command.trim.isEmpty) - Some("Command was empty.") - else - { - Control.trapUnit("Error running command: ", project.log) - { - val builder = new java.lang.ProcessBuilder((command :: args).toArray : _*).directory(project.info.projectDirectory) - val exitValue = Process(builder) ! log - if(exitValue == 0) - None - else - Some("Nonzero exit value (" + exitValue + ")") - } - } - } -} -private object TestScriptParser -{ - val SuccessLiteral = "success" - val Failure = "error" - val CommandStart = "$" - val ActionStart = ">" - val WordRegex = """[^ \[\]\s'\"][^ \[\]\s]*""".r - val StartRegex = ("[" + CommandStart + ActionStart + "]").r - - final def lazyFold[T](list: List[T])(f: T => Option[String]): Option[String] = - list match - { - case Nil => None - case head :: tail => - f(head) match - { - case None => lazyFold(tail)(f) - case x => x - } - } -} diff --git a/project/plugins/Plugins.scala b/project/plugins/Plugins.scala new file mode 100644 index 000000000..655940b99 --- /dev/null +++ b/project/plugins/Plugins.scala @@ -0,0 +1,6 @@ +import sbt._ + +class Plugins(info: ProjectInfo) extends PluginDefinition(info) +{ + val scripted = "org.scala-tools.sbt" % "test" % "0.5.1" +} \ No newline at end of file