From 8f444e66269e6498080f4cf03f0989a3bc242c2a Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 12 Jan 2011 21:44:18 +0000 Subject: Purely detabification of the process code. --- src/library/scala/sys/process/BasicIO.scala | 160 ++++++------- src/library/scala/sys/process/Process.scala | 98 ++++---- src/library/scala/sys/process/ProcessBuilder.scala | 162 ++++++------- .../scala/sys/process/ProcessBuilderImpl.scala | 192 ++++++++-------- src/library/scala/sys/process/ProcessIO.scala | 6 +- src/library/scala/sys/process/ProcessImpl.scala | 252 ++++++++++----------- src/library/scala/sys/process/ProcessLogger.scala | 6 +- 7 files changed, 438 insertions(+), 438 deletions(-) (limited to 'src') diff --git a/src/library/scala/sys/process/BasicIO.scala b/src/library/scala/sys/process/BasicIO.scala index 4f6a994b21..a761f9a75a 100644 --- a/src/library/scala/sys/process/BasicIO.scala +++ b/src/library/scala/sys/process/BasicIO.scala @@ -14,8 +14,8 @@ import java.io.{ BufferedReader, InputStreamReader, FilterInputStream, FilterOut import java.util.concurrent.LinkedBlockingQueue object BasicIO { - final val BufferSize = 8192 - final val Newline = props("line.separator") + final val BufferSize = 8192 + final val Newline = props("line.separator") private[process] final class Streamed[T]( val process: T => Unit, @@ -24,87 +24,87 @@ object BasicIO { ) private[process] object Streamed { - def apply[T](nonzeroException: Boolean): Streamed[T] = { - val q = new LinkedBlockingQueue[Either[Int, T]] - def next(): Stream[T] = q.take match { - case Left(0) => Stream.empty - case Left(code) => if (nonzeroException) sys.error("Nonzero exit code: " + code) else Stream.empty - case Right(s) => Stream.cons(s, next) - } - new Streamed((s: T) => q put Right(s), code => q put Left(code), () => next()) - } + def apply[T](nonzeroException: Boolean): Streamed[T] = { + val q = new LinkedBlockingQueue[Either[Int, T]] + def next(): Stream[T] = q.take match { + case Left(0) => Stream.empty + case Left(code) => if (nonzeroException) sys.error("Nonzero exit code: " + code) else Stream.empty + case Right(s) => Stream.cons(s, next) + } + new Streamed((s: T) => q put Right(s), code => q put Left(code), () => next()) + } } private[process] 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 + 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 } - def apply(withIn: Boolean, output: String => Unit, log: Option[ProcessLogger]) = - new ProcessIO(input(withIn), processFully(output), getErr(log)) - - def apply(withIn: Boolean, buffer: StringBuffer, log: Option[ProcessLogger]) = - new ProcessIO(input(withIn), processFully(buffer), getErr(log)) - - def apply(withIn: Boolean, log: ProcessLogger) = - new ProcessIO(input(withIn), processInfoFully(log), processErrFully(log)) - - def getErr(log: Option[ProcessLogger]) = log match { - case Some(lg) => processErrFully(lg) - case None => toStdErr - } - - private def processErrFully(log: ProcessLogger) = processFully(log error _) - private def processInfoFully(log: ProcessLogger) = processFully(log info _) - - def ignoreOut = (i: OutputStream) => () - - def close(c: Closeable) = try c.close() catch { case _: IOException => () } - def processFully(buffer: Appendable): InputStream => Unit = processFully(appendLine(buffer)) - def processFully(processLine: String => Unit): InputStream => Unit = in => { - val reader = new BufferedReader(new InputStreamReader(in)) - processLinesFully(processLine)(reader.readLine) - } - - def processLinesFully(processLine: String => Unit)(readLine: () => String) { - def readFully() { - val line = readLine() - if (line != null) { - processLine(line) - readFully() - } - } - readFully() - } - def connectToIn(o: OutputStream): Unit = transferFully(System.in, o) - def input(connect: Boolean): OutputStream => Unit = if (connect) connectToIn else ignoreOut - def standard(connectInput: Boolean): ProcessIO = standard(input(connectInput)) - def standard(in: OutputStream => Unit): ProcessIO = new ProcessIO(in, toStdOut, toStdErr) - - def toStdErr = (in: InputStream) => transferFully(in, System.err) - def toStdOut = (in: InputStream) => transferFully(in, System.out) - - def transferFully(in: InputStream, out: OutputStream): Unit = - try transferFullyImpl(in, out) - catch onInterrupt(()) - - private[this] def appendLine(buffer: Appendable): String => Unit = line => { - buffer append line - buffer append Newline - } - - private[this] def transferFullyImpl(in: InputStream, out: OutputStream) { - val buffer = new Array[Byte](BufferSize) - def loop() { - val byteCount = in.read(buffer) - if (byteCount > 0) { - out.write(buffer, 0, byteCount) - out.flush() - loop() - } - } - loop() - } + def apply(withIn: Boolean, output: String => Unit, log: Option[ProcessLogger]) = + new ProcessIO(input(withIn), processFully(output), getErr(log)) + + def apply(withIn: Boolean, buffer: StringBuffer, log: Option[ProcessLogger]) = + new ProcessIO(input(withIn), processFully(buffer), getErr(log)) + + def apply(withIn: Boolean, log: ProcessLogger) = + new ProcessIO(input(withIn), processInfoFully(log), processErrFully(log)) + + def getErr(log: Option[ProcessLogger]) = log match { + case Some(lg) => processErrFully(lg) + case None => toStdErr + } + + private def processErrFully(log: ProcessLogger) = processFully(log error _) + private def processInfoFully(log: ProcessLogger) = processFully(log info _) + + def ignoreOut = (i: OutputStream) => () + + def close(c: Closeable) = try c.close() catch { case _: IOException => () } + def processFully(buffer: Appendable): InputStream => Unit = processFully(appendLine(buffer)) + def processFully(processLine: String => Unit): InputStream => Unit = in => { + val reader = new BufferedReader(new InputStreamReader(in)) + processLinesFully(processLine)(reader.readLine) + } + + def processLinesFully(processLine: String => Unit)(readLine: () => String) { + def readFully() { + val line = readLine() + if (line != null) { + processLine(line) + readFully() + } + } + readFully() + } + def connectToIn(o: OutputStream): Unit = transferFully(System.in, o) + def input(connect: Boolean): OutputStream => Unit = if (connect) connectToIn else ignoreOut + def standard(connectInput: Boolean): ProcessIO = standard(input(connectInput)) + def standard(in: OutputStream => Unit): ProcessIO = new ProcessIO(in, toStdOut, toStdErr) + + def toStdErr = (in: InputStream) => transferFully(in, System.err) + def toStdOut = (in: InputStream) => transferFully(in, System.out) + + def transferFully(in: InputStream, out: OutputStream): Unit = + try transferFullyImpl(in, out) + catch onInterrupt(()) + + private[this] def appendLine(buffer: Appendable): String => Unit = line => { + buffer append line + buffer append Newline + } + + private[this] def transferFullyImpl(in: InputStream, out: OutputStream) { + val buffer = new Array[Byte](BufferSize) + def loop() { + val byteCount = in.read(buffer) + if (byteCount > 0) { + out.write(buffer, 0, byteCount) + out.flush() + loop() + } + } + loop() + } } diff --git a/src/library/scala/sys/process/Process.scala b/src/library/scala/sys/process/Process.scala index c64bb95fcf..c6a1f0d23f 100644 --- a/src/library/scala/sys/process/Process.scala +++ b/src/library/scala/sys/process/Process.scala @@ -16,70 +16,70 @@ import ProcessBuilder._ * It may be a compound process with several underlying native processes (such as 'a #&& b`). */ trait Process { - /** Blocks until this process exits and returns the exit code.*/ - def exitValue(): Int - /** Destroys this process. */ - def destroy(): Unit + /** Blocks until this process exits and returns the exit code.*/ + def exitValue(): Int + /** Destroys this process. */ + def destroy(): Unit } /** Methods for constructing simple commands that can then be combined. */ object Process extends ProcessImpl with ProcessCreation { } trait ProcessCreation { - def apply(command: String): ProcessBuilder = apply(command, None) - def apply(command: Seq[String]): ProcessBuilder = apply(command, None) - def apply(command: String, arguments: Seq[String]): ProcessBuilder = apply(command +: arguments, None) + def apply(command: String): ProcessBuilder = apply(command, None) + def apply(command: Seq[String]): ProcessBuilder = apply(command, None) + def apply(command: String, arguments: Seq[String]): ProcessBuilder = apply(command +: arguments, None) - /** create ProcessBuilder with working dir set to File and extra environment variables */ - def apply(command: String, cwd: File, extraEnv: (String, String)*): ProcessBuilder = - apply(command, Some(cwd), extraEnv: _*) + /** create ProcessBuilder with working dir set to File and extra environment variables */ + def apply(command: String, cwd: File, extraEnv: (String, String)*): ProcessBuilder = + apply(command, Some(cwd), extraEnv: _*) - /** create ProcessBuilder with working dir set to File and extra environment variables */ - def apply(command: Seq[String], cwd: File, extraEnv: (String, String)*): ProcessBuilder = - apply(command, Some(cwd), extraEnv: _*) + /** create ProcessBuilder with working dir set to File and extra environment variables */ + def apply(command: Seq[String], cwd: File, extraEnv: (String, String)*): ProcessBuilder = + apply(command, Some(cwd), extraEnv: _*) - /** create ProcessBuilder with working dir optionaly set to File and extra environment variables */ - def apply(command: String, cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = { - apply(command.split("""\s+"""), cwd, extraEnv : _*) - // not smart to use this on windows, because CommandParser uses \ to escape ". - /*CommandParser.parse(command) match { - case Left(errorMsg) => error(errorMsg) - case Right((cmd, args)) => apply(cmd :: args, cwd, extraEnv : _*) - }*/ - } + /** create ProcessBuilder with working dir optionaly set to File and extra environment variables */ + def apply(command: String, cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = { + apply(command.split("""\s+"""), cwd, extraEnv : _*) + // not smart to use this on windows, because CommandParser uses \ to escape ". + /*CommandParser.parse(command) match { + case Left(errorMsg) => error(errorMsg) + case Right((cmd, args)) => apply(cmd :: args, cwd, extraEnv : _*) + }*/ + } - /** create ProcessBuilder with working dir optionaly set to File and extra environment variables */ - def apply(command: Seq[String], cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = { - val jpb = new JProcessBuilder(command.toArray: _*) - cwd foreach (jpb directory _) - extraEnv foreach { case (k, v) => jpb.environment.put(k, v) } - apply(jpb) - } + /** create ProcessBuilder with working dir optionaly set to File and extra environment variables */ + def apply(command: Seq[String], cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = { + val jpb = new JProcessBuilder(command.toArray: _*) + cwd foreach (jpb directory _) + extraEnv foreach { case (k, v) => jpb.environment.put(k, v) } + apply(jpb) + } - def apply(builder: JProcessBuilder): ProcessBuilder = new Simple(builder) - def apply(file: File): FileBuilder = new FileImpl(file) - def apply(url: URL): URLBuilder = new URLImpl(url) - 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(builder: JProcessBuilder): ProcessBuilder = new Simple(builder) + def apply(file: File): FileBuilder = new FileImpl(file) + def apply(url: URL): URLBuilder = new URLImpl(url) + 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 Dummy(name, exitValue) - def applySeq[T](builders: Seq[T])(implicit convert: T => Source): Seq[Source] = builders.map(convert) + def apply(name: String, exitValue: => Int): ProcessBuilder = new Dummy(name, exitValue) + def applySeq[T](builders: Seq[T])(implicit convert: T => Source): Seq[Source] = builders.map(convert) - def cat(file: Source, files: Source*): ProcessBuilder = cat(file +: files) - def cat(files: Seq[Source]): ProcessBuilder = { - require(files.nonEmpty) - files map (_.cat) reduceLeft (_ #&& _) - } + def cat(file: Source, files: Source*): ProcessBuilder = cat(file +: files) + def cat(files: Seq[Source]): ProcessBuilder = { + require(files.nonEmpty) + files map (_.cat) reduceLeft (_ #&& _) + } } trait ProcessImplicits { - import Process._ + import Process._ - implicit def buildersToProcess[T](builders: Seq[T])(implicit convert: T => Source): Seq[Source] = applySeq(builders) - implicit def builderToProcess(builder: JProcessBuilder): ProcessBuilder = apply(builder) - implicit def fileToProcess(file: File): FileBuilder = apply(file) - implicit def urlToProcess(url: URL): URLBuilder = apply(url) - implicit def xmlToProcess(command: scala.xml.Elem): ProcessBuilder = apply(command) - implicit def stringToProcess(command: String): ProcessBuilder = apply(command) - implicit def stringSeqToProcess(command: Seq[String]): ProcessBuilder = apply(command) + implicit def buildersToProcess[T](builders: Seq[T])(implicit convert: T => Source): Seq[Source] = applySeq(builders) + implicit def builderToProcess(builder: JProcessBuilder): ProcessBuilder = apply(builder) + implicit def fileToProcess(file: File): FileBuilder = apply(file) + implicit def urlToProcess(url: URL): URLBuilder = apply(url) + implicit def xmlToProcess(command: scala.xml.Elem): ProcessBuilder = apply(command) + implicit def stringToProcess(command: String): ProcessBuilder = apply(command) + implicit def stringSeqToProcess(command: Seq[String]): ProcessBuilder = apply(command) } diff --git a/src/library/scala/sys/process/ProcessBuilder.scala b/src/library/scala/sys/process/ProcessBuilder.scala index cef269aba2..aff36bb456 100644 --- a/src/library/scala/sys/process/ProcessBuilder.scala +++ b/src/library/scala/sys/process/ProcessBuilder.scala @@ -14,64 +14,64 @@ import ProcessBuilder._ /** Represents a runnable process. */ trait ProcessBuilder extends Source with Sink { - /** Starts the process represented by this builder, blocks until it exits, and returns the output as a String. Standard error is - * sent to the console. If the exit code is non-zero, an exception is thrown.*/ - def !! : String - /** Starts the process represented by this builder, blocks until it exits, and returns the output as a String. Standard error is - * sent to the provided ProcessLogger. If the exit code is non-zero, an exception is thrown.*/ - def !!(log: ProcessLogger): String - /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available - * but the process has not completed. Standard error is sent to the console. If the process exits with a non-zero value, - * the Stream will provide all lines up to termination and then throw an exception. */ - def lines: Stream[String] - /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available - * but the process has not completed. Standard error is sent to the provided ProcessLogger. If the process exits with a non-zero value, - * the Stream will provide all lines up to termination but will not throw an exception. */ - def lines(log: ProcessLogger): Stream[String] - /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available - * but the process has not completed. Standard error is sent to the console. If the process exits with a non-zero value, - * the Stream will provide all lines up to termination but will not throw an exception. */ - def lines_! : Stream[String] - /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available - * but the process has not completed. Standard error is sent to the provided ProcessLogger. If the process exits with a non-zero value, - * the Stream will provide all lines up to termination but will not throw an exception. */ - def lines_!(log: ProcessLogger): Stream[String] - /** 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.*/ - def ! : Int - /** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are - * sent to the given ProcessLogger.*/ - def !(log: ProcessLogger): Int - /** 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. The newly started process reads from standard input of the current process.*/ - def !< : Int - /** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are - * sent to the given ProcessLogger. The newly started process reads from standard input of the current process.*/ - def !<(log: ProcessLogger): Int - /** Starts the process represented by this builder. Standard output and error are sent to the console.*/ - def run(): Process - /** Starts the process represented by this builder. Standard output and error are sent to the given ProcessLogger.*/ - def run(log: ProcessLogger): Process - /** Starts the process represented by this builder. I/O is handled by the given ProcessIO instance.*/ - def run(io: ProcessIO): Process - /** Starts the process represented by this builder. Standard output and error are sent to the console. - * The newly started process reads from standard input of the current process if `connectInput` is true.*/ - def run(connectInput: Boolean): Process - /** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are - * sent to the given ProcessLogger. - * The newly started process reads from standard input of the current process if `connectInput` is true.*/ - def run(log: ProcessLogger, connectInput: Boolean): Process + /** Starts the process represented by this builder, blocks until it exits, and returns the output as a String. Standard error is + * sent to the console. If the exit code is non-zero, an exception is thrown.*/ + def !! : String + /** Starts the process represented by this builder, blocks until it exits, and returns the output as a String. Standard error is + * sent to the provided ProcessLogger. If the exit code is non-zero, an exception is thrown.*/ + def !!(log: ProcessLogger): String + /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available + * but the process has not completed. Standard error is sent to the console. If the process exits with a non-zero value, + * the Stream will provide all lines up to termination and then throw an exception. */ + def lines: Stream[String] + /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available + * but the process has not completed. Standard error is sent to the provided ProcessLogger. If the process exits with a non-zero value, + * the Stream will provide all lines up to termination but will not throw an exception. */ + def lines(log: ProcessLogger): Stream[String] + /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available + * but the process has not completed. Standard error is sent to the console. If the process exits with a non-zero value, + * the Stream will provide all lines up to termination but will not throw an exception. */ + def lines_! : Stream[String] + /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available + * but the process has not completed. Standard error is sent to the provided ProcessLogger. If the process exits with a non-zero value, + * the Stream will provide all lines up to termination but will not throw an exception. */ + def lines_!(log: ProcessLogger): Stream[String] + /** 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.*/ + def ! : Int + /** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are + * sent to the given ProcessLogger.*/ + def !(log: ProcessLogger): Int + /** 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. The newly started process reads from standard input of the current process.*/ + def !< : Int + /** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are + * sent to the given ProcessLogger. The newly started process reads from standard input of the current process.*/ + def !<(log: ProcessLogger): Int + /** Starts the process represented by this builder. Standard output and error are sent to the console.*/ + def run(): Process + /** Starts the process represented by this builder. Standard output and error are sent to the given ProcessLogger.*/ + def run(log: ProcessLogger): Process + /** Starts the process represented by this builder. I/O is handled by the given ProcessIO instance.*/ + def run(io: ProcessIO): Process + /** Starts the process represented by this builder. Standard output and error are sent to the console. + * The newly started process reads from standard input of the current process if `connectInput` is true.*/ + def run(connectInput: Boolean): Process + /** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are + * sent to the given ProcessLogger. + * The newly started process reads from standard input of the current process if `connectInput` is true.*/ + def run(log: ProcessLogger, connectInput: Boolean): Process - /** Constructs a command that runs this command first and then `other` if this command succeeds.*/ - def #&& (other: ProcessBuilder): ProcessBuilder - /** Constructs a command that runs this command first and then `other` if this command does not succeed.*/ - def #|| (other: ProcessBuilder): ProcessBuilder - /** Constructs a command that will run this command and pipes the output to `other`. `other` must be a simple command.*/ - 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 + /** Constructs a command that runs this command first and then `other` if this command succeeds.*/ + def #&& (other: ProcessBuilder): ProcessBuilder + /** Constructs a command that runs this command first and then `other` if this command does not succeed.*/ + def #|| (other: ProcessBuilder): ProcessBuilder + /** Constructs a command that will run this command and pipes the output to `other`. `other` must be a simple command.*/ + 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 - def canPipeTo: Boolean + def canPipeTo: Boolean } object ProcessBuilder extends ProcessBuilderImpl { @@ -79,35 +79,35 @@ object ProcessBuilder extends ProcessBuilderImpl { } trait FileBuilder extends Sink with Source { - def #<<(f: File): ProcessBuilder - def #<<(u: URL): ProcessBuilder - def #<<(i: => InputStream): ProcessBuilder - def #<<(p: ProcessBuilder): ProcessBuilder + def #<<(f: File): ProcessBuilder + def #<<(u: URL): ProcessBuilder + def #<<(i: => InputStream): ProcessBuilder + def #<<(p: ProcessBuilder): ProcessBuilder } trait Source { protected def toSource: 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 OStreamBuilder(out, "")) - def #>(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(toSource, b, false) - def cat = toSource - private def toFile(f: File, append: Boolean) = #> (new FileOutput(f, append)) + /** 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 OStreamBuilder(out, "")) + def #>(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(toSource, b, false) + def cat = toSource + private def toFile(f: File, append: Boolean) = #> (new FileOutput(f, append)) } trait Sink { - protected def toSink: 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 IStreamBuilder(in, "")) - def #<(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(b, toSink, false) + protected def toSink: 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 IStreamBuilder(in, "")) + def #<(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(b, toSink, false) } } diff --git a/src/library/scala/sys/process/ProcessBuilderImpl.scala b/src/library/scala/sys/process/ProcessBuilderImpl.scala index 8bb6627e9c..23cdefae34 100644 --- a/src/library/scala/sys/process/ProcessBuilderImpl.scala +++ b/src/library/scala/sys/process/ProcessBuilderImpl.scala @@ -19,8 +19,8 @@ private[process] trait ProcessBuilderImpl { self: ProcessBuilder.type => private[process] class Dummy(override val toString: String, exitValue: => Int) extends AbstractBuilder { - override def run(io: ProcessIO): Process = new DummyProcess(exitValue) - override def canPipeTo = true + override def run(io: ProcessIO): Process = new DummyProcess(exitValue) + override def canPipeTo = true } private[process] class URLInput(url: URL) extends IStreamBuilder(url.openStream, url.toString) @@ -42,117 +42,117 @@ private[process] trait ProcessBuilderImpl { runImpl: ProcessIO => Unit ) extends AbstractBuilder { - override def run(io: ProcessIO): Process = { - val success = new SyncVar[Boolean] - success put false - val t = Spawn { - runImpl(io) - success set true - } + override def run(io: ProcessIO): Process = { + val success = new SyncVar[Boolean] + success put false + val t = Spawn { + runImpl(io) + success set true + } - new ThreadProcess(t, success) - } + new ThreadProcess(t, success) + } } /** Represents a simple command without any redirection or combination. */ private[process] class Simple(p: JProcessBuilder) extends AbstractBuilder { - override def run(io: ProcessIO): Process = { - val process = p.start() // start the external process - import io._ - - // spawn threads that process the input, output, and error streams using the functions defined in `io` - val inThread = Spawn(writeInput(process.getOutputStream), true) - val outThread = Spawn(processOutput(process.getInputStream)) - val errorThread = - if (p.redirectErrorStream) Nil - else List(Spawn(processError(process.getErrorStream))) - - new SimpleProcess(process, inThread, outThread :: errorThread) - } - override def toString = p.command.toString - override def canPipeTo = true + override def run(io: ProcessIO): Process = { + val process = p.start() // start the external process + import io._ + + // spawn threads that process the input, output, and error streams using the functions defined in `io` + val inThread = Spawn(writeInput(process.getOutputStream), true) + val outThread = Spawn(processOutput(process.getInputStream)) + val errorThread = + if (p.redirectErrorStream) Nil + else List(Spawn(processError(process.getErrorStream))) + + new SimpleProcess(process, inThread, outThread :: errorThread) + } + override def toString = p.command.toString + override def canPipeTo = true } private[process] abstract class AbstractBuilder extends ProcessBuilder with Sink with Source { - protected def toSource = this - protected def toSink = this + protected def toSource = this + protected def toSink = this - def #|(other: ProcessBuilder): ProcessBuilder = { - require(other.canPipeTo, "Piping to multiple processes is not supported.") - new PipedBuilder(this, other, false) - } - def #||(other: ProcessBuilder): ProcessBuilder = new OrBuilder(this, other) - def #&&(other: ProcessBuilder): ProcessBuilder = new AndBuilder(this, other) - def ###(other: ProcessBuilder): ProcessBuilder = new SequenceBuilder(this, other) + def #|(other: ProcessBuilder): ProcessBuilder = { + require(other.canPipeTo, "Piping to multiple processes is not supported.") + new PipedBuilder(this, other, false) + } + def #||(other: ProcessBuilder): ProcessBuilder = new OrBuilder(this, other) + def #&&(other: ProcessBuilder): ProcessBuilder = new AndBuilder(this, other) + def ###(other: ProcessBuilder): ProcessBuilder = new SequenceBuilder(this, other) def run(): Process = run(false) def run(connectInput: Boolean): Process = run(BasicIO.standard(connectInput)) def run(log: ProcessLogger): Process = run(log, false) def run(log: ProcessLogger, connectInput: Boolean): Process = run(BasicIO(connectInput, log)) - def !! = slurp(None, false) - def !!(log: ProcessLogger) = slurp(Some(log), false) - def !!< = slurp(None, true) - def !!<(log: ProcessLogger) = slurp(Some(log), true) - - def lines: Stream[String] = lines(false, true, None) - def lines(log: ProcessLogger): Stream[String] = lines(false, true, Some(log)) - def lines_! : Stream[String] = lines(false, false, None) - def lines_!(log: ProcessLogger): Stream[String] = lines(false, false, Some(log)) - - def ! = run(false).exitValue() - def !(io: ProcessIO) = run(io).exitValue() - def !(log: ProcessLogger) = runBuffered(log, false) - def !< = run(true).exitValue() - def !<(log: ProcessLogger) = runBuffered(log, true) - - private[this] def slurp(log: Option[ProcessLogger], withIn: Boolean): String = { - val buffer = new StringBuffer - val code = this ! BasicIO(withIn, buffer, log) - - if (code == 0) buffer.toString - else sys.error("Nonzero exit value: " + code) - } - - private[this] def lines( - withInput: Boolean, - nonZeroException: Boolean, - log: Option[ProcessLogger] - ): Stream[String] = { - val streamed = Streamed[String](nonZeroException) - val process = run(BasicIO(withInput, streamed.process, log)) - - Spawn(streamed done process.exitValue()) - streamed.stream() - } - - private[this] def runBuffered(log: ProcessLogger, connectInput: Boolean) = - log buffer run(log, connectInput).exitValue() - - def canPipeTo = false + def !! = slurp(None, false) + def !!(log: ProcessLogger) = slurp(Some(log), false) + def !!< = slurp(None, true) + def !!<(log: ProcessLogger) = slurp(Some(log), true) + + def lines: Stream[String] = lines(false, true, None) + def lines(log: ProcessLogger): Stream[String] = lines(false, true, Some(log)) + def lines_! : Stream[String] = lines(false, false, None) + def lines_!(log: ProcessLogger): Stream[String] = lines(false, false, Some(log)) + + def ! = run(false).exitValue() + def !(io: ProcessIO) = run(io).exitValue() + def !(log: ProcessLogger) = runBuffered(log, false) + def !< = run(true).exitValue() + def !<(log: ProcessLogger) = runBuffered(log, true) + + private[this] def slurp(log: Option[ProcessLogger], withIn: Boolean): String = { + val buffer = new StringBuffer + val code = this ! BasicIO(withIn, buffer, log) + + if (code == 0) buffer.toString + else sys.error("Nonzero exit value: " + code) + } + + private[this] def lines( + withInput: Boolean, + nonZeroException: Boolean, + log: Option[ProcessLogger] + ): Stream[String] = { + val streamed = Streamed[String](nonZeroException) + val process = run(BasicIO(withInput, streamed.process, log)) + + Spawn(streamed done process.exitValue()) + streamed.stream() + } + + private[this] def runBuffered(log: ProcessLogger, connectInput: Boolean) = + log buffer run(log, connectInput).exitValue() + + def canPipeTo = false } private[process] class URLImpl(url: URL) extends URLBuilder with Source { - protected def toSource = new URLInput(url) + protected def toSource = new URLInput(url) } private[process] class FileImpl(base: File) extends FileBuilder with Sink with Source { - protected def toSource = new FileInput(base) - protected def toSink = new FileOutput(base, 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 IStreamBuilder(s, "")) - def #<<(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(b, new FileOutput(base, true), false) + def #<<(f: File): ProcessBuilder = #<<(new FileInput(f)) + def #<<(u: URL): ProcessBuilder = #<<(new URLInput(u)) + def #<<(s: => InputStream): ProcessBuilder = #<<(new IStreamBuilder(s, "")) + def #<<(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(b, new FileOutput(base, true), false) } private[process] abstract class BasicBuilder extends AbstractBuilder { - protected[this] def checkNotThis(a: ProcessBuilder) = require(a != this, "Compound process '" + a + "' cannot contain itself.") - final def run(io: ProcessIO): Process = { - val p = createProcess(io) - p.start() - p - } - protected[this] def createProcess(io: ProcessIO): BasicProcess + protected[this] def checkNotThis(a: ProcessBuilder) = require(a != this, "Compound process '" + a + "' cannot contain itself.") + final def run(io: ProcessIO): Process = { + val p = createProcess(io) + p.start() + p + } + protected[this] def createProcess(io: ProcessIO): BasicProcess } private[process] abstract class SequentialBuilder( @@ -161,9 +161,9 @@ private[process] trait ProcessBuilderImpl { operatorString: String ) extends BasicBuilder { - checkNotThis(a) - checkNotThis(b) - override def toString = " ( " + a + " " + operatorString + " " + b + " ) " + checkNotThis(a) + checkNotThis(b) + override def toString = " ( " + a + " " + operatorString + " " + b + " ) " } private[process] class PipedBuilder( @@ -172,27 +172,27 @@ private[process] trait ProcessBuilderImpl { toError: Boolean ) extends SequentialBuilder(first, second, if (toError) "#|!" else "#|") { - override def createProcess(io: ProcessIO) = new PipedProcesses(first, second, io, toError) + override def createProcess(io: ProcessIO) = new PipedProcesses(first, second, io, toError) } private[process] class AndBuilder( first: ProcessBuilder, second: ProcessBuilder ) extends SequentialBuilder(first, second, "#&&") { - override def createProcess(io: ProcessIO) = new AndProcess(first, second, io) + override def createProcess(io: ProcessIO) = new AndProcess(first, second, io) } private[process] class OrBuilder( first: ProcessBuilder, second: ProcessBuilder ) extends SequentialBuilder(first, second, "#||") { - override def createProcess(io: ProcessIO) = new OrProcess(first, second, io) + override def createProcess(io: ProcessIO) = new OrProcess(first, second, io) } private[process] class SequenceBuilder( first: ProcessBuilder, second: ProcessBuilder ) extends SequentialBuilder(first, second, "###") { - override def createProcess(io: ProcessIO) = new ProcessSequence(first, second, io) + override def createProcess(io: ProcessIO) = new ProcessSequence(first, second, io) } } \ No newline at end of file diff --git a/src/library/scala/sys/process/ProcessIO.scala b/src/library/scala/sys/process/ProcessIO.scala index db9c8b3823..a71f6b416c 100644 --- a/src/library/scala/sys/process/ProcessIO.scala +++ b/src/library/scala/sys/process/ProcessIO.scala @@ -17,7 +17,7 @@ final class ProcessIO( val processOutput: InputStream => Unit, val processError: InputStream => Unit ) { - def withInput(write: OutputStream => Unit): ProcessIO = new ProcessIO(write, processOutput, processError) - def withOutput(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, process, processError) - def withError(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, processOutput, process) + def withInput(write: OutputStream => Unit): ProcessIO = new ProcessIO(write, processOutput, processError) + def withOutput(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, process, processError) + def withError(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, processOutput, process) } diff --git a/src/library/scala/sys/process/ProcessImpl.scala b/src/library/scala/sys/process/ProcessImpl.scala index 23b0ffc266..7ed30585fa 100644 --- a/src/library/scala/sys/process/ProcessImpl.scala +++ b/src/library/scala/sys/process/ProcessImpl.scala @@ -17,28 +17,28 @@ private[process] trait ProcessImpl { /** Runs provided code in a new Thread and returns the Thread instance. */ private[process] object Spawn { - def apply(f: => Unit): Thread = apply(f, false) - def apply(f: => Unit, daemon: Boolean): Thread = { - val thread = new Thread() { override def run() = { f } } - thread.setDaemon(daemon) - thread.start() - thread - } + def apply(f: => Unit): Thread = apply(f, false) + def apply(f: => Unit, daemon: Boolean): Thread = { + val thread = new Thread() { override def run() = { f } } + thread.setDaemon(daemon) + thread.start() + thread + } } private[process] object Future { - def apply[T](f: => T): () => T = { - val result = new SyncVar[Either[Throwable, T]] - def run: Unit = - try result set Right(f) - catch { case e: Exception => result set Left(e) } - - Spawn(run) - - () => result.get match { - case Right(value) => value - case Left(exception) => throw exception - } - } + def apply[T](f: => T): () => T = { + val result = new SyncVar[Either[Throwable, T]] + def run: Unit = + try result set Right(f) + catch { case e: Exception => result set Left(e) } + + Spawn(run) + + () => result.get match { + case Right(value) => value + case Left(exception) => throw exception + } + } } private[process] class AndProcess( @@ -66,86 +66,86 @@ private[process] trait ProcessImpl { evaluateSecondProcess: Int => Boolean ) extends CompoundProcess { - protected[this] override def runAndExitValue() = { - val first = a.run(io) - runInterruptible(first.exitValue)(first.destroy()) flatMap { codeA => - if (evaluateSecondProcess(codeA)) { - val second = b.run(io) - runInterruptible(second.exitValue)(second.destroy()) - } - else Some(codeA) - } - } + protected[this] override def runAndExitValue() = { + val first = a.run(io) + runInterruptible(first.exitValue)(first.destroy()) flatMap { codeA => + if (evaluateSecondProcess(codeA)) { + val second = b.run(io) + runInterruptible(second.exitValue)(second.destroy()) + } + else Some(codeA) + } + } } private[process] abstract class BasicProcess extends Process { - def start(): Unit + def start(): Unit } private[process] abstract class CompoundProcess extends BasicProcess { - def destroy() = destroyer() - def exitValue() = getExitValue() getOrElse sys.error("No exit code: process destroyed.") - def start() = getExitValue - - protected lazy val (getExitValue, destroyer) = { - val code = new SyncVar[Option[Int]]() - code set None - val thread = Spawn(code set runAndExitValue()) - - ( - Future { thread.join(); code.get }, - () => thread.interrupt() - ) - } - - /** Start and block until the exit value is available and then return it in Some. Return None if destroyed (use 'run')*/ - protected[this] def runAndExitValue(): Option[Int] - - protected[this] def runInterruptible[T](action: => T)(destroyImpl: => Unit): Option[T] = { - try Some(action) - catch onInterrupt { destroyImpl; None } - } + def destroy() = destroyer() + def exitValue() = getExitValue() getOrElse sys.error("No exit code: process destroyed.") + def start() = getExitValue + + protected lazy val (getExitValue, destroyer) = { + val code = new SyncVar[Option[Int]]() + code set None + val thread = Spawn(code set runAndExitValue()) + + ( + Future { thread.join(); code.get }, + () => thread.interrupt() + ) + } + + /** Start and block until the exit value is available and then return it in Some. Return None if destroyed (use 'run')*/ + protected[this] def runAndExitValue(): Option[Int] + + protected[this] def runInterruptible[T](action: => T)(destroyImpl: => Unit): Option[T] = { + try Some(action) + catch onInterrupt { destroyImpl; None } + } } private[process] class PipedProcesses(a: ProcessBuilder, b: ProcessBuilder, defaultIO: ProcessIO, toError: Boolean) extends CompoundProcess { - protected[this] override def runAndExitValue() = { - val currentSource = new SyncVar[Option[InputStream]] - val pipeOut = new PipedOutputStream - val source = new PipeSource(currentSource, pipeOut, a.toString) - source.start() - - val pipeIn = new PipedInputStream(pipeOut) - val currentSink = new SyncVar[Option[OutputStream]] - val sink = new PipeSink(pipeIn, currentSink, b.toString) - sink.start() - - def handleOutOrError(fromOutput: InputStream) = currentSource put Some(fromOutput) - - val firstIO = - if (toError) - defaultIO.withError(handleOutOrError) - else - defaultIO.withOutput(handleOutOrError) - val secondIO = defaultIO.withInput(toInput => currentSink put Some(toInput)) - - val second = b.run(secondIO) - val first = a.run(firstIO) - try { - runInterruptible { - first.exitValue - currentSource put None - currentSink put None - second.exitValue - } { - first.destroy() - second.destroy() - } - } - finally { - BasicIO close pipeIn - BasicIO close pipeOut - } - } + protected[this] override def runAndExitValue() = { + val currentSource = new SyncVar[Option[InputStream]] + val pipeOut = new PipedOutputStream + val source = new PipeSource(currentSource, pipeOut, a.toString) + source.start() + + val pipeIn = new PipedInputStream(pipeOut) + val currentSink = new SyncVar[Option[OutputStream]] + val sink = new PipeSink(pipeIn, currentSink, b.toString) + sink.start() + + def handleOutOrError(fromOutput: InputStream) = currentSource put Some(fromOutput) + + val firstIO = + if (toError) + defaultIO.withError(handleOutOrError) + else + defaultIO.withOutput(handleOutOrError) + val secondIO = defaultIO.withInput(toInput => currentSink put Some(toInput)) + + val second = b.run(secondIO) + val first = a.run(firstIO) + try { + runInterruptible { + first.exitValue + currentSource put None + currentSink put None + second.exitValue + } { + first.destroy() + second.destroy() + } + } + finally { + BasicIO close pipeIn + BasicIO close pipeOut + } + } } private[process] abstract class PipeThread(isSink: Boolean, labelFn: () => String) extends Thread { @@ -170,15 +170,15 @@ private[process] trait ProcessImpl { label: => String ) extends PipeThread(false, () => label) { - final override def run(): Unit = currentSource.get match { - case Some(source) => - try runloop(source, pipe) - finally currentSource.unset() + final override def run(): Unit = currentSource.get match { + case Some(source) => + try runloop(source, pipe) + finally currentSource.unset() - run() - case None => - currentSource.unset() - BasicIO close pipe + run() + case None => + currentSource.unset() + BasicIO close pipe } } private[process] class PipeSink( @@ -187,23 +187,23 @@ private[process] trait ProcessImpl { label: => String ) extends PipeThread(true, () => label) { - final override def run(): Unit = currentSink.get match { - case Some(sink) => - try runloop(pipe, sink) - finally currentSink.unset() + final override def run(): Unit = currentSink.get match { + case Some(sink) => + try runloop(pipe, sink) + finally currentSink.unset() - run() - case None => - currentSink.unset() - } + run() + case None => + currentSink.unset() + } } /** A thin wrapper around a java.lang.Process. `ioThreads` are the Threads created to do I/O. * The implementation of `exitValue` waits until these threads die before returning. */ private[process] class DummyProcess(action: => Int) extends Process { - private[this] val exitCode = Future(action) - override def exitValue() = exitCode() - override def destroy() { } + private[this] val exitCode = Future(action) + override def exitValue() = exitCode() + override def destroy() { } } /** A thin wrapper around a java.lang.Process. `outputThreads` are the Threads created to read from the * output and error streams of the process. `inputThread` is the Thread created to write to the input stream of @@ -211,23 +211,23 @@ private[process] trait ProcessImpl { * The implementation of `exitValue` interrupts `inputThread` and then waits until all I/O threads die before * returning. */ private[process] class SimpleProcess(p: JProcess, inputThread: Thread, outputThreads: List[Thread]) extends Process { - override def exitValue() = { - try p.waitFor() // wait for the process to terminate - finally inputThread.interrupt() // we interrupt the input thread to notify it that it can terminate - outputThreads foreach (_.join()) // this ensures that all output is complete before returning (waitFor does not ensure this) - - p.exitValue() - } - override def destroy() = { - try p.destroy() - finally inputThread.interrupt() - } + override def exitValue() = { + try p.waitFor() // wait for the process to terminate + finally inputThread.interrupt() // we interrupt the input thread to notify it that it can terminate + outputThreads foreach (_.join()) // this ensures that all output is complete before returning (waitFor does not ensure this) + + p.exitValue() + } + override def destroy() = { + try p.destroy() + finally inputThread.interrupt() + } } private[process] final class ThreadProcess(thread: Thread, success: SyncVar[Boolean]) extends Process { - override def exitValue() = { - thread.join() - if (success.get) 0 else 1 - } - override def destroy() { thread.interrupt() } + override def exitValue() = { + thread.join() + if (success.get) 0 else 1 + } + override def destroy() { thread.interrupt() } } } diff --git a/src/library/scala/sys/process/ProcessLogger.scala b/src/library/scala/sys/process/ProcessLogger.scala index 107921d66a..4f0c2779c7 100644 --- a/src/library/scala/sys/process/ProcessLogger.scala +++ b/src/library/scala/sys/process/ProcessLogger.scala @@ -10,7 +10,7 @@ package scala.sys package process trait ProcessLogger { - def info(s: => String): Unit - def error(s: => String): Unit - def buffer[T](f: => T): T + def info(s: => String): Unit + def error(s: => String): Unit + def buffer[T](f: => T): T } -- cgit v1.2.3