From 0685320bd58e2fa3e04a1aec632df52e1e9e4512 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Fri, 26 Mar 2010 08:22:06 -0400 Subject: [PATCH] * fix buffered logging * script tasks --- sbt/project/build/ReleaseProject.scala | 96 --------------------- sbt/src/main/scala/sbt/Logger.scala | 9 +- sbt/src/main/scala/sbt/Path.scala | 13 +++ sbt/src/main/scala/sbt/Project.scala | 9 +- sbt/src/main/scala/sbt/script/Run.scala | 69 +++++++++++++++ sbt/src/main/scala/sbt/script/Scripts.scala | 35 ++++++++ 6 files changed, 129 insertions(+), 102 deletions(-) delete mode 100644 sbt/project/build/ReleaseProject.scala create mode 100644 sbt/src/main/scala/sbt/script/Run.scala create mode 100644 sbt/src/main/scala/sbt/script/Scripts.scala diff --git a/sbt/project/build/ReleaseProject.scala b/sbt/project/build/ReleaseProject.scala deleted file mode 100644 index d9ad31207..000000000 --- a/sbt/project/build/ReleaseProject.scala +++ /dev/null @@ -1,96 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -/*import sbt._ - -import java.io.File - -trait ReleaseProject extends ExecProject -{ self: SbtProject => - def info: ProjectInfo - lazy val releaseChecks = javaVersionCheck && projectVersionCheck && fullyCheckedOut - lazy val fullyCheckedOut = - task - { - if(svnArtifactsPath.exists) None - else Some("You need a fully checked out sbt repository with commit rights to do a release.") - } - lazy val javaVersionCheck = - task - { - val javaVersion = System.getProperty("java.version") - if(!javaVersion.startsWith("1.5.")) - Some("Java version must be 1.5.x (was " + javaVersion + ")") - else - None - } - lazy val projectVersionCheck = - task - { - def value(a: Option[Int]) = a.getOrElse(0) - def lessThan(a: Option[Int], b: Option[Int]) = value(a) < value(b) - version match - { - case BasicVersion(major, minor, micro, None) => - Version.fromString(sbtVersion.value) match - { - case Right(BasicVersion(builderMajor, builderMinor, builderMicro, None)) - if (builderMajor < major || ( builderMajor == major && - lessThan(builderMinor, minor) || (builderMinor == minor && - lessThan(builderMicro, micro ) ))) => - None - case _ => Some("Invalid builder sbt version. Must be a release version older than the project version. (was: " + sbtVersion.value + ")") - } - case _ => Some("Invalid project version. Should be of the form #.#.# (was: " + version + ")") - } - } - - def svnURL = "https://simple-build-tool.googlecode.com/svn/" - def latestURL = svnURL + "artifacts/latest" - - def svnArtifactsPath = Path.fromFile(info.projectPath.asFile.getParentFile) / "artifacts" - def svnArtifactPath = svnArtifactsPath / version.toString - def ivyLocalPath = Path.userHome / ".ivy2" / "local" / "sbt" / "simple-build-tool" / version.toString - def manualTasks = - ("Upload launcher jar: " + boot.outputJar.absolutePath) :: - "Update, build, check and commit Hello Lift example" :: - Nil - - lazy val copyDocs = main.copyTask ( (main.mainDocPath ##) ** "*", svnArtifactPath / "api") dependsOn(main.doc, releaseChecks) - lazy val copyIvysJars = main.copyTask( (ivyLocalPath ##) ** "*", svnArtifactPath) dependsOn(main.crossPublishLocal, releaseChecks) - - lazy val release = manualTaskMessage dependsOn(commitDocs, releaseArtifacts, releaseChecks) - lazy val releaseArtifacts = nextVersion dependsOn(tag, latestLink, boot.proguard, releaseChecks) - lazy val manualTaskMessage = task { println("The following tasks must be done manually:\n\t" + manualTasks.mkString("\n\t")); None } - - import sbt.ProcessXML._ - lazy val addArtifacts = execTask { svn add {svnArtifactPath} } dependsOn ( copyIvysJars, copyDocs, releaseChecks ) - lazy val commitArtifacts = execTask { svn commit -m "Jars, Ivys, and API Docs for {version.toString}" {svnArtifactPath} } dependsOn(addArtifacts, releaseChecks) - lazy val tag = execTask { svn copy -m "Tagging {version.toString}" {svnURL}/trunk/ {svnURL}/tags/{version.toString} } dependsOn(releaseChecks) - lazy val latestLink = (deleteLatestLink && makeLatestLink) dependsOn(commitArtifacts, releaseChecks) - lazy val makeLatestLink = execTask { svn copy -m "Creating new latest link" {svnURL}/artifacts/{version.toString}/ {latestURL} } dependsOn(releaseChecks) - lazy val deleteLatestLink = execTask { svn del -m "Deleting old latest link" {latestURL} } dependsOn(releaseChecks) - lazy val commitProperties = execTask { svn commit -m "Bumping versions" project/build.properties } dependsOn(releaseChecks) - lazy val commitDocs = execTask { svn commit -m "Updated documentation for {version.toString}" ../wiki/ } dependsOn(releaseChecks) - - lazy val nextVersion = (incrementVersions && commitProperties) dependsOn(releaseChecks) - lazy val incrementVersions = task { incrementVersionNumbers(); None } - def incrementVersionNumbers(): Unit = - for( v <- projectVersion) - { - sbtVersion() = v.toString - val incremented = v.asInstanceOf[BasicVersion].incrementMicro // BasicVersion checked by releaseChecks - import incremented._ - val newVersion = BasicVersion(major, minor, micro, Some("SNAPSHOT")) - log.info("Changing version to " + newVersion) - projectVersion() = newVersion - saveEnvironment - } -} - -package sbt { - object ProcessXML { - implicit def elemToPB(command: scala.xml.Elem): ProcessBuilder = - impl.CommandParser.parse(command.text.trim).fold(error, Function.tupled(Process.apply)) - } -}*/ \ No newline at end of file diff --git a/sbt/src/main/scala/sbt/Logger.scala b/sbt/src/main/scala/sbt/Logger.scala index b71f4b650..dfd3be94f 100644 --- a/sbt/src/main/scala/sbt/Logger.scala +++ b/sbt/src/main/scala/sbt/Logger.scala @@ -137,7 +137,7 @@ final class FilterLogger(delegate: Logger) extends BasicLogger if(atLevel(Level.Info)) delegate.control(event, message) } - def logAll(events: Seq[LogEvent]): Unit = events.foreach(delegate.log) + def logAll(events: Seq[LogEvent]): Unit = delegate.logAll(events) } /** A logger that can buffer the logging done on it by currently executing Thread and @@ -354,4 +354,11 @@ object Level extends Enumeration with NotNull def apply(s: String) = levels.find(s == _.toString) /** Same as apply, defined for use in pattern matching. */ private[sbt] def unapply(s: String) = apply(s) +} +final class LoggerWriter(delegate: Logger, level: Level.Value) extends java.io.Writer +{ + override def flush() {} + override def close() {} + override def write(content: Array[Char], offset: Int, length: Int): Unit = + delegate.log(level, new String(content, offset, length)) } \ No newline at end of file diff --git a/sbt/src/main/scala/sbt/Path.scala b/sbt/src/main/scala/sbt/Path.scala index 7a0e4e6b8..12fa4b329 100644 --- a/sbt/src/main/scala/sbt/Path.scala +++ b/sbt/src/main/scala/sbt/Path.scala @@ -55,6 +55,19 @@ sealed abstract class Path extends PathFinder with NotNull def absolutePath: String = asFile.getAbsolutePath private[sbt] def prependTo(s: String): String + /** The last component of this path.*/ + def name = asFile.getName + /** The extension part of the name of this path. This is the part of the name after the last period, or the empty string if there is no period.*/ + def ext = baseAndExt._2 + /** The base of the name of this path. This is the part of the name before the last period, or the full name if there is no period.*/ + def base = baseAndExt._1 + def baseAndExt: (String, String) = + { + val nme = name + val dot = nme.lastIndexOf('.') + if(dot < 0) (nme, "") else (nme.substring(0, dot), nme.substring(dot+1)) + } + /** Equality of Paths is defined in terms of the underlying File.*/ override final def equals(other: Any) = other match diff --git a/sbt/src/main/scala/sbt/Project.scala b/sbt/src/main/scala/sbt/Project.scala index aaf86eec4..39fa9a267 100644 --- a/sbt/src/main/scala/sbt/Project.scala +++ b/sbt/src/main/scala/sbt/Project.scala @@ -17,7 +17,7 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment final val log: Logger = logImpl protected def logImpl: Logger = { - val lg = new FilterLogger(new BufferedLogger(info.logger)) + val lg = new BufferedLogger(new FilterLogger(info.logger)) lg.setLevel(defaultLoggingLevel) lg } @@ -388,6 +388,7 @@ object Project /** Checks the project's dependencies, initializes its environment, and possibly its directories.*/ private def initialize[P <: Project](p: P, setupInfo: Option[SetupInfo], log: Logger): P = { + def save() = p.saveEnvironment() foreach { errorMsg => log.error(errorMsg) } setupInfo match { case Some(setup) => @@ -399,8 +400,7 @@ object Project p.projectOrganization() = org if(!setup.initializeDirectories) p.setEnvironmentModified(false) - for(errorMessage <- p.saveEnvironment()) - log.error(errorMessage) + save() if(setup.initializeDirectories) p.initializeDirectories() } @@ -409,8 +409,7 @@ object Project { p.initializeDirectories() p.projectInitialize() = false - for(errorMessage <- p.saveEnvironment()) - log.error(errorMessage) + save() } } val useName = p.projectName.get.getOrElse("at " + p.info.projectDirectory.getAbsolutePath) diff --git a/sbt/src/main/scala/sbt/script/Run.scala b/sbt/src/main/scala/sbt/script/Run.scala new file mode 100644 index 000000000..7e4281271 --- /dev/null +++ b/sbt/src/main/scala/sbt/script/Run.scala @@ -0,0 +1,69 @@ +package sbt.script + + import java.io.PrintWriter + import javax.script.{ScriptContext, ScriptEngine, ScriptEngineManager, SimpleScriptContext} + import xsbt.OpenResource.fileReader + import xsbt.FileUtilities.defaultCharset + +object Run +{ + def apply(file: Path, project: Project): AnyRef = + apply(file, defaultContext(project)_, project) + def apply(file: Path, ctx: ScriptContext => Unit, project: Project): AnyRef = + apply(file, ctx, defaultLoaders(project)) + def apply(script: String, language: String, project: Project): AnyRef = + apply(script, language, defaultContext(project)_, project) + def apply(script: String, language: String, ctx: ScriptContext => Unit, project: Project): AnyRef = + apply(script, language, ctx, defaultLoaders(project)) + + def apply(file: Path, ctx: ScriptContext => Unit, loaders: Stream[ClassLoader]): AnyRef = + { + val engine = getEngineByExt(loaders, file.ext) + ctx(engine.getContext) + apply(file, engine) + } + def apply(script: String, language: String, ctx: ScriptContext => Unit, loaders: Stream[ClassLoader]): AnyRef = + { + val engine = getEngineByName(loaders, language) + ctx(engine.getContext) + engine.eval(script) + } + + def defaultContext(project: Project)(ctx: ScriptContext) + { + ctx.setAttribute("project", project, ScriptContext.ENGINE_SCOPE) + // JavaScript implementation in 1.6 depends on this being a PrintWriter + ctx.setErrorWriter(new PrintWriter(new LoggerWriter(project.log, Level.Error))) + ctx.setWriter(new PrintWriter(new LoggerWriter(project.log, Level.Info))) + } + + def apply(file: Path, engine: ScriptEngine): AnyRef = + xsbt.OpenResource.fileReader(defaultCharset)(file asFile)( engine.eval ) + + def bind(engine: ScriptEngine, bindings: Map[String, AnyRef]): Unit = + for( (k, v) <- bindings ) engine.put(k, v) + + def defaultLoaders(project: Project) = Stream(project.getClass.getClassLoader, launcherLoader(project)) + def launcherLoader(project: Project) = project.info.launcher.topLoader.getParent + def defaultBindings(project: Project) = Map("project" -> project) + + def getEngine(loaders: Stream[ClassLoader], get: ScriptEngineManager => ScriptEngine, label: String): ScriptEngine = + firstEngine( engines(managers(loaders), get), label) + + def getEngineByName(loaders: Stream[ClassLoader], lang: String): ScriptEngine = + getEngine(loaders, _.getEngineByName(lang), "name '" + lang + "'") + def getEngineByExt(loaders: Stream[ClassLoader], ext: String): ScriptEngine = + getEngine(loaders, _.getEngineByExtension(ext), "extension '" + ext + "'") + + def managers(loaders: Stream[ClassLoader]): Stream[ScriptEngineManager] = + loaders.map(new ScriptEngineManager(_)) + def engines(managers: Stream[ScriptEngineManager], get: ScriptEngineManager => ScriptEngine) = + managers.flatMap(getEngine(get)) + def firstEngine(engines: Stream[ScriptEngine], label: String) = + engines.headOption.getOrElse(error("Could not find script engine for " + label)) + def getEngine(get: ScriptEngineManager => ScriptEngine)(manager: ScriptEngineManager) = + { + val engine = get(manager) + if(engine == null) Nil else engine :: Nil + } +} \ No newline at end of file diff --git a/sbt/src/main/scala/sbt/script/Scripts.scala b/sbt/src/main/scala/sbt/script/Scripts.scala new file mode 100644 index 000000000..da8f43951 --- /dev/null +++ b/sbt/src/main/scala/sbt/script/Scripts.scala @@ -0,0 +1,35 @@ +package sbt.script + + import scala.collection.Set + import Scripts.{checkName, runScript} + +trait Scripts extends Project +{ + abstract override def tasks = if(scriptedTasks.isEmpty) super.tasks else Map() ++ super.tasks ++ scriptedTasks + + def scriptSources: PathFinder = info.builderPath / "scripts" * "*.*" + + lazy val scriptedTasks: Map[String, Task] = makeScripted(scriptSources.get) + + def makeScripted(files: Set[Path]): Map[String, Task] = Map() ++ (files map makeScripted) + def makeScripted(file: Path): (String, Task) = + { + val t = scriptedTask(file) named(checkName(file.base)) + (t.name, t) + } + def scriptedTask(file: Path): Task = task { runScript(file, this) } + def scriptedTask(script: String, language: String): Task = task { runScript(script, language, this) } +} +object Scripts +{ + def runScript(file: Path, project: Project): Option[String] = getError( Run(file, project) ) + def runScript(script: String, language: String, project: Project): Option[String] = getError( Run(script, language, project) ) + def getError(result: AnyRef): Option[String] = + result match + { + case Some(v: String) => Some(v) + case _ => None + } + def checkName(base: String) = base.find(c => !legalID(c)) match { case Some(c) => error("Illegal character in scripted task name '" + base + "': '" + c + "'"); case None => base } + def legalID(c: Char) = java.lang.Character.isJavaIdentifierPart(c) || c == '-' +}