mirror of https://github.com/sbt/sbt.git
* fixed tab completion for method tasks for multi-project builds
* check that tasks in compoundTask do not reference static tasks * make toString of Paths in subprojects relative to root project directory * crossScalaVersions is now inherited from parent * Added scala-library.jar to the javac classpath * Added Process.cat that will send contents of URLs and Files to standard output git-svn-id: https://simple-build-tool.googlecode.com/svn/trunk@877 d89573ee-9141-11dd-94d4-bdf5e562f29c
This commit is contained in:
parent
f2fe9bcfe3
commit
8420606a8c
|
|
@ -3,10 +3,16 @@
|
|||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
|
||||
object CompileOrder extends Enumeration
|
||||
{
|
||||
val Mixed, JavaThenScala, ScalaThenJava = Value
|
||||
}
|
||||
private object CompilerCore
|
||||
{
|
||||
def scalaClasspathForJava = FileUtilities.scalaJars.map(_.getAbsolutePath).mkString(File.pathSeparator)
|
||||
}
|
||||
sealed abstract class CompilerCore
|
||||
{
|
||||
val ClasspathOptionString = "-classpath"
|
||||
|
|
@ -16,25 +22,35 @@ sealed abstract class CompilerCore
|
|||
protected def process(args: List[String], log: Logger): Boolean
|
||||
// Returns false if there were errors, true if there were not.
|
||||
protected def processJava(args: List[String], log: Logger): Boolean = true
|
||||
protected def scalaClasspathForJava: String
|
||||
def actionStartMessage(label: String): String
|
||||
def actionNothingToDoMessage: String
|
||||
def actionSuccessfulMessage: String
|
||||
def actionUnsuccessfulMessage: String
|
||||
|
||||
private def classpathString(rawClasspathString: String, includeScala: Boolean) =
|
||||
if(includeScala)
|
||||
List(rawClasspathString, scalaClasspathForJava).mkString(File.pathSeparator)
|
||||
else
|
||||
rawClasspathString
|
||||
|
||||
final def apply(label: String, sources: Iterable[Path], classpathString: String, outputDirectory: Path, options: Seq[String], log: Logger): Option[String] =
|
||||
apply(label, sources, classpathString, outputDirectory, options, Nil, CompileOrder.Mixed, log)
|
||||
final def apply(label: String, sources: Iterable[Path], classpathString: String, outputDirectory: Path, options: Seq[String], javaOptions: Seq[String], order: CompileOrder.Value, log: Logger): Option[String] =
|
||||
final def apply(label: String, sources: Iterable[Path], rawClasspathString: String, outputDirectory: Path, options: Seq[String], javaOptions: Seq[String], order: CompileOrder.Value, log: Logger): Option[String] =
|
||||
{
|
||||
log.info(actionStartMessage(label))
|
||||
val classpathOption: List[String] =
|
||||
if(classpathString.isEmpty)
|
||||
def classpathOption(includeScala: Boolean): List[String] =
|
||||
{
|
||||
val classpath = classpathString(rawClasspathString, includeScala)
|
||||
if(classpath.isEmpty)
|
||||
Nil
|
||||
else
|
||||
List(ClasspathOptionString, classpathString)
|
||||
List(ClasspathOptionString, classpath)
|
||||
}
|
||||
val outputDir = outputDirectory.asFile
|
||||
FileUtilities.createDirectory(outputDir, log) orElse
|
||||
{
|
||||
val classpathAndOut: List[String] = OutputOptionString :: outputDir.getAbsolutePath :: classpathOption
|
||||
def classpathAndOut(javac: Boolean): List[String] = OutputOptionString :: outputDir.getAbsolutePath :: classpathOption(javac)
|
||||
|
||||
Control.trapUnit("Compiler error: ", log)
|
||||
{
|
||||
|
|
@ -47,7 +63,7 @@ sealed abstract class CompilerCore
|
|||
else
|
||||
{
|
||||
def filteredSources(extension: String) = sourceList.filter(_.endsWith(extension))
|
||||
def compile(label: String, sources: List[String], options: Seq[String])(process: (List[String], Logger) => Boolean) =
|
||||
def compile(label: String, sources: List[String], options: Seq[String], includeScala: Boolean)(process: (List[String], Logger) => Boolean) =
|
||||
{
|
||||
if(sources.isEmpty)
|
||||
{
|
||||
|
|
@ -56,7 +72,7 @@ sealed abstract class CompilerCore
|
|||
}
|
||||
else
|
||||
{
|
||||
val arguments = (options ++ classpathAndOut ++ sources).toList
|
||||
val arguments = (options ++ classpathAndOut(includeScala) ++ sources).toList
|
||||
log.debug(label + " arguments: " + arguments.mkString(" "))
|
||||
process(arguments, log)
|
||||
}
|
||||
|
|
@ -64,12 +80,12 @@ sealed abstract class CompilerCore
|
|||
def scalaCompile = () =>
|
||||
{
|
||||
val scalaSourceList = if(order == CompileOrder.Mixed) sourceList else filteredSources(".scala")
|
||||
compile("Scala", scalaSourceList, options)(process)
|
||||
compile("Scala", scalaSourceList, options, false)(process)
|
||||
}
|
||||
def javaCompile = () =>
|
||||
{
|
||||
val javaSourceList = filteredSources(".java")
|
||||
compile("Java", javaSourceList, javaOptions)(processJava)
|
||||
compile("Java", javaSourceList, javaOptions, true)(processJava)
|
||||
}
|
||||
|
||||
val (first, second) = if(order == CompileOrder.JavaThenScala) (javaCompile, scalaCompile) else (scalaCompile, javaCompile)
|
||||
|
|
@ -100,6 +116,7 @@ final class ForkCompile(config: ForkScalaCompiler) extends CompilerBase
|
|||
Fork.scalac(config.javaHome, config.compileJVMOptions, config.scalaJars, arguments, log) == 0
|
||||
override protected def processJava(args: List[String], log: Logger) =
|
||||
Fork.javac(config.javaHome, args, log) == 0
|
||||
override protected def scalaClasspathForJava = config.scalaJars.mkString(File.pathSeparator)
|
||||
}
|
||||
object ForkCompile
|
||||
{
|
||||
|
|
@ -152,9 +169,11 @@ final class Compile(maximumErrors: Int) extends CompilerBase
|
|||
}
|
||||
override protected def processJava(args: List[String], log: Logger) =
|
||||
(Process("javac", args) ! log) == 0
|
||||
protected def scalaClasspathForJava = CompilerCore.scalaClasspathForJava
|
||||
}
|
||||
final class Scaladoc(maximumErrors: Int) extends CompilerCore
|
||||
{
|
||||
protected def scalaClasspathForJava = CompilerCore.scalaClasspathForJava
|
||||
protected def process(arguments: List[String], log: Logger) =
|
||||
{
|
||||
import scala.tools.nsc.{doc, CompilerCommand, FatalError, Global, reporters, util}
|
||||
|
|
|
|||
|
|
@ -803,6 +803,7 @@ object FileUtilities
|
|||
lazy val sbtJar: File = classLocationFile(getClass)
|
||||
lazy val scalaLibraryJar: File = classLocationFile[scala.ScalaObject]
|
||||
lazy val scalaCompilerJar: File = classLocationFile[scala.tools.nsc.Settings]
|
||||
def scalaJars: Iterable[File] = List(scalaLibraryJar, scalaCompilerJar)
|
||||
|
||||
/** The producer of randomness for unique name generation.*/
|
||||
private val random = new java.util.Random
|
||||
|
|
|
|||
|
|
@ -309,8 +309,8 @@ object ManageDependencies
|
|||
configSet.toArray
|
||||
}
|
||||
}
|
||||
def excludeScalaJar(name: String)
|
||||
{ module.addExcludeRule(excludeRule(ScalaArtifacts.Organization, name, configurationNames)) }
|
||||
def excludeScalaJar(name: String): Unit =
|
||||
module.addExcludeRule(excludeRule(ScalaArtifacts.Organization, name, configurationNames))
|
||||
excludeScalaJar(ScalaArtifacts.LibraryID)
|
||||
excludeScalaJar(ScalaArtifacts.CompilerID)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,13 +82,16 @@ private[sbt] final class FilePath(file: File) extends Path
|
|||
def projectRelativePathString(separator: String) = relativePathString(separator)
|
||||
private[sbt] def prependTo(s: String) = absolutePath + sep + s
|
||||
}
|
||||
private[sbt] final class ProjectDirectory(file: File) extends Path
|
||||
// toRoot is the path between this and the root project path and is used for toString
|
||||
private[sbt] final class ProjectDirectory(file: File, toRoot: Option[Path]) extends Path
|
||||
{
|
||||
def this(file: File) = this(file, None)
|
||||
lazy val asFile = absolute(file)
|
||||
override def toString = "."
|
||||
override def toString = foldToRoot(_.toString, ".")
|
||||
def relativePathString(separator: String) = ""
|
||||
def projectRelativePathString(separator: String) = ""
|
||||
private[sbt] def prependTo(s: String) = "." + sep + s
|
||||
private[sbt] def prependTo(s: String) = foldToRoot(_.prependTo(s), "." + sep + s)
|
||||
private[sbt] def foldToRoot[T](f: Path => T, orElse: T) = toRoot.map(f).getOrElse(orElse)
|
||||
}
|
||||
private[sbt] final class RelativePath(val parentPath: Path, val component: String) extends Path
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,23 +20,51 @@ object Process
|
|||
implicit def apply(command: scala.xml.Elem): ProcessBuilder = apply(command.text.trim)
|
||||
def apply(value: Boolean): ProcessBuilder = apply(value.toString, if(value) 0 else 1)
|
||||
def apply(name: String, exitValue: => Int): ProcessBuilder = new DummyProcessBuilder(name, exitValue)
|
||||
|
||||
def cat(file: SourcePartialBuilder, files: SourcePartialBuilder*): ProcessBuilder = cat(file :: files.toList)
|
||||
private[this] def cat(files: Seq[SourcePartialBuilder]): ProcessBuilder =
|
||||
{
|
||||
require(!files.isEmpty)
|
||||
files.map(_.cat).reduceLeft(_ #&& _)
|
||||
}
|
||||
}
|
||||
|
||||
trait URLPartialBuilder extends NotNull
|
||||
trait SourcePartialBuilder extends NotNull
|
||||
{
|
||||
def #>(b: ProcessBuilder): ProcessBuilder
|
||||
def #>>(b: File): ProcessBuilder
|
||||
def #>(b: File): ProcessBuilder
|
||||
/** Writes the output stream of this process to the given file. */
|
||||
def #> (f: File): ProcessBuilder = toFile(f, false)
|
||||
/** Appends the output stream of this process to the given file. */
|
||||
def #>> (f: File): ProcessBuilder = toFile(f, true)
|
||||
/** Writes the output stream of this process to the given OutputStream. The
|
||||
* argument is call-by-name, so the stream is recreated, written, and closed each
|
||||
* time this process is executed. */
|
||||
def #>(out: => OutputStream): ProcessBuilder = #> (new OutputStreamBuilder(out))
|
||||
def #>(b: ProcessBuilder): ProcessBuilder = new PipedProcessBuilder(toSource, b, false)
|
||||
private def toFile(f: File, append: Boolean) = #> (new FileOutput(f, append))
|
||||
def cat = toSource
|
||||
protected def toSource: ProcessBuilder
|
||||
}
|
||||
trait FilePartialBuilder extends NotNull
|
||||
trait SinkPartialBuilder extends NotNull
|
||||
{
|
||||
def #>(b: ProcessBuilder): ProcessBuilder
|
||||
def #<(b: ProcessBuilder): ProcessBuilder
|
||||
def #<(url: URL): ProcessBuilder
|
||||
def #>>(b: File): ProcessBuilder
|
||||
def #>(b: File): ProcessBuilder
|
||||
def #<(file: File): ProcessBuilder
|
||||
def #<<(file: File): ProcessBuilder
|
||||
/** Reads the given file into the input stream of this process. */
|
||||
def #< (f: File): ProcessBuilder = #< (new FileInput(f))
|
||||
/** Reads the given URL into the input stream of this process. */
|
||||
def #< (f: URL): ProcessBuilder = #< (new URLInput(f))
|
||||
/** Reads the given InputStream into the input stream of this process. The
|
||||
* argument is call-by-name, so the stream is recreated, read, and closed each
|
||||
* time this process is executed. */
|
||||
def #<(in: => InputStream): ProcessBuilder = #< (new InputStreamBuilder(in))
|
||||
def #<(b: ProcessBuilder): ProcessBuilder = new PipedProcessBuilder(b, toSink, false)
|
||||
protected def toSink: ProcessBuilder
|
||||
}
|
||||
|
||||
trait URLPartialBuilder extends SourcePartialBuilder
|
||||
trait FilePartialBuilder extends SinkPartialBuilder with SourcePartialBuilder
|
||||
{
|
||||
def #<<(f: File): ProcessBuilder
|
||||
def #<<(u: URL): ProcessBuilder
|
||||
def #<<(i: => InputStream): ProcessBuilder
|
||||
def #<<(p: ProcessBuilder): ProcessBuilder
|
||||
}
|
||||
|
||||
/** Represents a process that is running or has finished running.
|
||||
|
|
@ -49,7 +77,7 @@ trait Process extends NotNull
|
|||
def destroy(): Unit
|
||||
}
|
||||
/** Represents a runnable process. */
|
||||
trait ProcessBuilder extends NotNull
|
||||
trait ProcessBuilder extends SourcePartialBuilder with SinkPartialBuilder
|
||||
{
|
||||
/** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are
|
||||
* sent to the console.*/
|
||||
|
|
@ -85,14 +113,6 @@ trait ProcessBuilder extends NotNull
|
|||
def #| (other: ProcessBuilder): ProcessBuilder
|
||||
/** Constructs a command that will run this command and then `other`. The exit code will be the exit code of `other`.*/
|
||||
def ## (other: ProcessBuilder): ProcessBuilder
|
||||
/** Reads the given file into the input stream of this process. */
|
||||
def #< (f: File): ProcessBuilder
|
||||
/** Reads the given URL into the input stream of this process. */
|
||||
def #< (f: URL): ProcessBuilder
|
||||
/** Writes the output stream of this process to the given file. */
|
||||
def #> (f: File): ProcessBuilder
|
||||
/** Appends the output stream of this process to the given file. */
|
||||
def #>> (f: File): ProcessBuilder
|
||||
|
||||
def canPipeTo: Boolean
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
* are different tasks with the same name, only one will be included. */
|
||||
def taskList: String = descriptionList(deepTasks)
|
||||
|
||||
final def taskName(task: Task) = tasks.find( _._2 eq task ).map(_._1).getOrElse(UnnamedName)
|
||||
final def taskName(task: Task) = tasks.find( _._2 eq task ).map(_._1)
|
||||
/** A description of all available tasks in this project and all dependencies and all
|
||||
* available method tasks in this project, but not of dependencies. If there
|
||||
* are different tasks or methods with the same name, only one will be included. */
|
||||
|
|
@ -233,7 +233,12 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
protected def disableCrossPaths = crossScalaVersions.isEmpty
|
||||
/** By default, this is empty and cross-building is disabled. Overriding this to a Set of Scala versions
|
||||
* will enable cross-building against those versions.*/
|
||||
def crossScalaVersions = scala.collection.immutable.Set.empty[String]
|
||||
def crossScalaVersions: immutable.Set[String] =
|
||||
info.parent match
|
||||
{
|
||||
case Some(p) => p.crossScalaVersions
|
||||
case None => immutable.Set.empty[String]
|
||||
}
|
||||
/** A `PathFinder` that determines the files watched when an action is run with a preceeding ~ when this is the current
|
||||
* project. This project does not need to include the watched paths for projects that this project depends on.*/
|
||||
def watchPaths: PathFinder = Path.emptyPathFinder
|
||||
|
|
@ -256,7 +261,6 @@ private[sbt] final class LoadSetupError(val message: String) extends LoadResult
|
|||
|
||||
object Project
|
||||
{
|
||||
val UnnamedName = "<anonymous>"
|
||||
val BootDirectoryName = "boot"
|
||||
val DefaultOutputDirectoryName = "target"
|
||||
val DefaultEnvBackingName = "build.properties"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@ import FileUtilities._
|
|||
|
||||
final case class ProjectInfo(projectDirectory: File, dependencies: Iterable[Project], parent: Option[Project]) extends NotNull
|
||||
{
|
||||
val projectPath = new ProjectDirectory(projectDirectory)
|
||||
val projectPath: Path =
|
||||
{
|
||||
val toRoot = parent.flatMap(p => Path.relativize(p.info.projectPath, projectDirectory))
|
||||
new ProjectDirectory(projectDirectory, toRoot)
|
||||
}
|
||||
val builderPath = projectPath / ProjectInfo.MetadataDirectoryName
|
||||
def bootPath = builderPath / Project.BootDirectoryName
|
||||
def builderProjectPath = builderPath / Project.BuilderProjectDirectoryName
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
*/
|
||||
package sbt
|
||||
|
||||
import TaskManager._
|
||||
|
||||
trait Described extends NotNull
|
||||
{
|
||||
def description: Option[String]
|
||||
|
|
@ -19,7 +21,8 @@ trait TaskManager{
|
|||
/** Creates a method task that executes the given action when invoked. */
|
||||
def task(action: Array[String] => ManagedTask) = new MethodTask(None, action, Nil)
|
||||
|
||||
def taskName(t: Task): String
|
||||
def taskName(t: Task): Option[String]
|
||||
final def taskNameString(task: Task): String = taskName(task).getOrElse(UnnamedName)
|
||||
|
||||
/** A method task is an action that has parameters. Note that it is not a Task, though,
|
||||
* because it requires arguments to perform its work. It therefore cannot be a dependency of
|
||||
|
|
@ -42,7 +45,8 @@ trait TaskManager{
|
|||
this(None, description, dependencies, interactive, action)
|
||||
checkTaskDependencies(dependencies)
|
||||
def manager: ManagerType = TaskManager.this
|
||||
def name = explicitName.getOrElse(taskName(this))
|
||||
def name = explicitName.getOrElse(implicitName)
|
||||
private[sbt] def implicitName = taskNameString(this)
|
||||
def named(name: String) = construct(Some(name), description,dependencies, interactive, action)
|
||||
override def toString = "Task " + name
|
||||
|
||||
|
|
@ -77,8 +81,16 @@ trait TaskManager{
|
|||
interactive: Boolean, action : => Option[String]) = new CompoundTask(explicitName, description, dependencies, interactive, action, createWork)
|
||||
def work = createWork
|
||||
}
|
||||
def compoundTask(createTask: => Project#Task) = new CompoundTask(SubWork[Project#Task](createTask))
|
||||
def dynamic(createTask: => Project#Task) = new CompoundTask(SubWork[Project#Task](checkDynamic(createTask)))
|
||||
|
||||
/** Verifies that the given dynamically created task does not depend on any statically defined tasks.
|
||||
* Returns the task if it is valid.*/
|
||||
private def checkDynamic(task: Project#Task) =
|
||||
{
|
||||
for(t <- task.topologicalSort; staticName <- t.implicitName)
|
||||
error("Dynamic task " + task.name + " depends on static task " + staticName)
|
||||
task
|
||||
}
|
||||
private def checkTaskDependencies(dependencyList: List[ManagedTask])
|
||||
{
|
||||
val nullDependencyIndex = dependencyList.findIndexOf(_ == null)
|
||||
|
|
@ -87,3 +99,7 @@ trait TaskManager{
|
|||
require(interactiveDependencyIndex < 0, "Dependency (at index " + interactiveDependencyIndex + ") is interactive. Interactive tasks cannot be dependencies.")
|
||||
}
|
||||
}
|
||||
object TaskManager
|
||||
{
|
||||
val UnnamedName = "<anonymous>"
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ package sbt
|
|||
|
||||
import java.lang.{Process => JProcess, ProcessBuilder => JProcessBuilder}
|
||||
import java.io.{BufferedReader, Closeable, InputStream, InputStreamReader, IOException, OutputStream, PrintStream}
|
||||
import java.io.{PipedInputStream, PipedOutputStream}
|
||||
import java.io.{FilterInputStream, FilterOutputStream, PipedInputStream, PipedOutputStream}
|
||||
import java.io.{File, FileInputStream, FileOutputStream}
|
||||
import java.net.URL
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ private object BasicIO
|
|||
}
|
||||
|
||||
|
||||
private abstract class AbstractProcessBuilder extends ProcessBuilder
|
||||
private abstract class AbstractProcessBuilder extends ProcessBuilder with SinkPartialBuilder with SourcePartialBuilder
|
||||
{
|
||||
def #&&(other: ProcessBuilder): ProcessBuilder = new AndProcessBuilder(this, other)
|
||||
def #||(other: ProcessBuilder): ProcessBuilder = new OrProcessBuilder(this, other)
|
||||
|
|
@ -90,10 +90,8 @@ private abstract class AbstractProcessBuilder extends ProcessBuilder
|
|||
}
|
||||
def ##(other: ProcessBuilder): ProcessBuilder = new SequenceProcessBuilder(this, other)
|
||||
|
||||
def #< (f: File): ProcessBuilder = new PipedProcessBuilder(new FileInput(f), this, false)
|
||||
def #< (url: URL): ProcessBuilder = new PipedProcessBuilder(new URLInput(url), this, false)
|
||||
def #> (f: File): ProcessBuilder = new PipedProcessBuilder(this, new FileOutput(f, false), false)
|
||||
def #>> (f: File): ProcessBuilder = new PipedProcessBuilder(this, new FileOutput(f, true), true)
|
||||
protected def toSource = this
|
||||
protected def toSink = this
|
||||
|
||||
def run(): Process = run(false)
|
||||
def run(connectInput: Boolean): Process = run(BasicIO.standard(connectInput))
|
||||
|
|
@ -115,23 +113,19 @@ private abstract class AbstractProcessBuilder extends ProcessBuilder
|
|||
|
||||
def canPipeTo = false
|
||||
}
|
||||
private[sbt] class URLBuilder(url: URL) extends URLPartialBuilder
|
||||
|
||||
private[sbt] class URLBuilder(url: URL) extends URLPartialBuilder with SourcePartialBuilder
|
||||
{
|
||||
def #>(b: ProcessBuilder): ProcessBuilder = b #< url
|
||||
def #>>(file: File): ProcessBuilder = toFile(file, true)
|
||||
def #>(file: File): ProcessBuilder = toFile(file, false)
|
||||
private def toFile(file: File, append: Boolean) = new PipedProcessBuilder(new URLInput(url), new FileOutput(file, append), false)
|
||||
protected def toSource = new URLInput(url)
|
||||
}
|
||||
private[sbt] class FileBuilder(base: File) extends FilePartialBuilder
|
||||
private[sbt] class FileBuilder(base: File) extends FilePartialBuilder with SinkPartialBuilder with SourcePartialBuilder
|
||||
{
|
||||
def #>(b: ProcessBuilder): ProcessBuilder = b #< base
|
||||
def #<(b: ProcessBuilder): ProcessBuilder = b #> base
|
||||
def #<(url: URL): ProcessBuilder = new URLBuilder(url) #> base
|
||||
def #>>(file: File): ProcessBuilder = pipe(base, file, true)
|
||||
def #>(file: File): ProcessBuilder = pipe(base, file, false)
|
||||
def #<(file: File): ProcessBuilder = pipe(file, base, false)
|
||||
def #<<(file: File): ProcessBuilder = pipe(file, base, true)
|
||||
private def pipe(from: File, to: File, append: Boolean) = new PipedProcessBuilder(new FileInput(from), new FileOutput(to, append), false)
|
||||
protected def toSource = new FileInput(base)
|
||||
protected def toSink = new FileOutput(base, false)
|
||||
def #<<(f: File): ProcessBuilder = #<<(new FileInput(f))
|
||||
def #<<(u: URL): ProcessBuilder = #<<(new URLInput(u))
|
||||
def #<<(s: => InputStream): ProcessBuilder = #<<(new InputStreamBuilder(s))
|
||||
def #<<(b: ProcessBuilder): ProcessBuilder = new PipedProcessBuilder(b, new FileOutput(base, true), false)
|
||||
}
|
||||
|
||||
private abstract class BasicBuilder extends AbstractProcessBuilder
|
||||
|
|
@ -369,8 +363,15 @@ private class FileOutput(file: File, append: Boolean) extends OutputStreamBuilde
|
|||
private class URLInput(url: URL) extends InputStreamBuilder(url.openStream, url.toString)
|
||||
private class FileInput(file: File) extends InputStreamBuilder(new FileInputStream(file), file.getAbsolutePath)
|
||||
|
||||
private class OutputStreamBuilder(stream: => OutputStream, label: String) extends ThreadProcessBuilder(label, _.writeInput(stream))
|
||||
private class InputStreamBuilder(stream: => InputStream, label: String) extends ThreadProcessBuilder(label, _.processOutput(stream))
|
||||
import Uncloseable.protect
|
||||
private class OutputStreamBuilder(stream: => OutputStream, label: String) extends ThreadProcessBuilder(label, _.writeInput(protect(stream)))
|
||||
{
|
||||
def this(stream: => OutputStream) = this(stream, "<output stream>")
|
||||
}
|
||||
private class InputStreamBuilder(stream: => InputStream, label: String) extends ThreadProcessBuilder(label, _.processOutput(protect(stream)))
|
||||
{
|
||||
def this(stream: => InputStream) = this(stream, "<input stream>")
|
||||
}
|
||||
|
||||
private abstract class ThreadProcessBuilder(override val toString: String, runImpl: ProcessIO => Unit) extends AbstractProcessBuilder
|
||||
{
|
||||
|
|
@ -381,7 +382,7 @@ private abstract class ThreadProcessBuilder(override val toString: String, runIm
|
|||
new ThreadProcess(Spawn {runImpl(io); success.set(true) }, success)
|
||||
}
|
||||
}
|
||||
private class ThreadProcess(thread: Thread, success: SyncVar[Boolean]) extends Process
|
||||
private final class ThreadProcess(thread: Thread, success: SyncVar[Boolean]) extends Process
|
||||
{
|
||||
override def exitValue() =
|
||||
{
|
||||
|
|
@ -389,4 +390,12 @@ private class ThreadProcess(thread: Thread, success: SyncVar[Boolean]) extends P
|
|||
if(success.get) 0 else 1
|
||||
}
|
||||
override def destroy() { thread.interrupt() }
|
||||
}
|
||||
|
||||
object Uncloseable
|
||||
{
|
||||
def apply(in: InputStream): InputStream = new FilterInputStream(in) { override def close() {} }
|
||||
def apply(out: OutputStream): OutputStream = new FilterOutputStream(out) { override def close() {} }
|
||||
def protect(in: InputStream): InputStream = if(in eq System.in) Uncloseable(in) else in
|
||||
def protect(out: OutputStream): OutputStream = if( (out eq System.out) || (out eq System.err)) Uncloseable(out) else out
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ object ProcessSpecification extends Properties("Process I/O")
|
|||
specify("Correct exit code", (exitCode: Byte) => checkExit(exitCode))
|
||||
specify("#&& correct", (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ #&& _)(_ && _))
|
||||
specify("#|| correct", (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ #|| _)(_ || _))
|
||||
specify("## correct", (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ ## _)( (x,latest) => latest))
|
||||
specify("Pipe to output file", (data: Array[Byte]) => checkFileOut(data))
|
||||
specify("Pipe to input file", (data: Array[Byte]) => checkFileIn(data))
|
||||
specify("Pipe to process", (data: Array[Byte]) => checkPipe(data))
|
||||
|
|
@ -28,7 +29,7 @@ object ProcessSpecification extends Properties("Process I/O")
|
|||
}
|
||||
}
|
||||
private def toBoolean(exitCode: Int) = exitCode == 0
|
||||
private def checkExit(code: Byte) =
|
||||
private def checkExit(code: Byte) =
|
||||
{
|
||||
val exitCode = unsigned(code)
|
||||
(process("sbt.exit " + exitCode) !) == exitCode
|
||||
|
|
|
|||
Loading…
Reference in New Issue