diff --git a/src/library/scala/sys/process/BasicIO.scala b/src/library/scala/sys/process/BasicIO.scala
index 9c5deaccb..b8881db9f 100644
--- a/src/library/scala/sys/process/BasicIO.scala
+++ b/src/library/scala/sys/process/BasicIO.scala
@@ -14,6 +14,12 @@ import java.io.{ BufferedReader, InputStreamReader, FilterInputStream, FilterOut
import java.util.concurrent.LinkedBlockingQueue
import scala.collection.immutable.Stream
+/**
+ * This object contains factories for [[scala.sys.process.ProcessIO]],
+ * which can be used to control the I/O of a [[scala.sys.process.Process]]
+ * when a [[scala.sys.process.ProcessBuilder]] is started with the `run`
+ * command.
+ */
object BasicIO {
final val BufferSize = 8192
final val Newline = props("line.separator")
diff --git a/src/library/scala/sys/process/Process.scala b/src/library/scala/sys/process/Process.scala
index 73152a4a7..4e4ffb5b8 100644
--- a/src/library/scala/sys/process/Process.scala
+++ b/src/library/scala/sys/process/Process.scala
@@ -14,6 +14,19 @@ import ProcessBuilder._
/** Represents a process that is running or has finished running.
* It may be a compound process with several underlying native processes (such as 'a #&& b`).
+ *
+ * This trait is often not used directly, though its companion object contains
+ * factories for [[scala.sys.process.ProcessBuilder]], the main component of this
+ * package.
+ *
+ * It is used directly when calling the method `run` on a `ProcessBuilder`,
+ * which makes the process run in the background. The methods provided on `Process`
+ * make it possible for one to block until the process exits and get the exit value,
+ * or destroy the process altogether.
+ *
+ * Presently, one cannot poll the `Process` to see if it has finished.
+ *
+ * @see [[scala.sys.process.ProcessBuilder]]
*/
trait Process {
/** Blocks until this process exits and returns the exit code.*/
@@ -25,20 +38,52 @@ trait Process {
/** Methods for constructing simple commands that can then be combined. */
object Process extends ProcessImpl with ProcessCreation { }
+/** Factories for creating [[scala.sys.process.ProcessBuilder]]. They can be
+ * found on and used through [[scala.sys.process.Process]]'s companion object.
+ */
trait ProcessCreation {
+ /** Create a [[scala.sys.process.ProcessBuilder]] from a `String`, including the
+ * parameters.
+ *
+ * @example {{{ apply("cat file.txt") }}}
+ */
def apply(command: String): ProcessBuilder = apply(command, None)
+
+ /** Create a [[scala.sys.process.ProcessBuilder]] from a sequence of `String`,
+ * where the head is the command and each element of the tail is a parameter.
+ *
+ * @example {{{ apply("cat" :: files) }}}
+ */
def apply(command: Seq[String]): ProcessBuilder = apply(command, None)
+
+ /** Create a [[scala.sys.process.ProcessBuilder]] from a command represented by a `String`,
+ * and a sequence of `String` representing the arguments.
+ *
+ * @example {{{ apply("cat", files) }}}
+ */
def apply(command: String, arguments: Seq[String]): ProcessBuilder = apply(command +: arguments, None)
- /** create ProcessBuilder with working dir set to File and extra environment variables */
+ /** Create a [[scala.sys.process.ProcessBuilder]] with working dir set to `File` and extra
+ * environment variables.
+ *
+ * @example {{{ apply("java", new java.ioFile("/opt/app"), "CLASSPATH" -> "library.jar") }}}
+ */
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 */
+ /** Create a [[scala.sys.process.ProcessBuilder]] with working dir set to `File` and extra
+ * environment variables.
+ *
+ * @example {{{ apply("java" :: javaArgs, new java.ioFile("/opt/app"), "CLASSPATH" -> "library.jar") }}}
+ */
def apply(command: Seq[String], cwd: File, extraEnv: (String, String)*): ProcessBuilder =
apply(command, Some(cwd), extraEnv: _*)
- /** create ProcessBuilder with working dir optionally set to File and extra environment variables */
+ /** Create a [[scala.sys.process.ProcessBuilder]] with working dir optionally set to
+ * `File` and extra environment variables.
+ *
+ * @example {{{ apply("java", params.get("cwd"), "CLASSPATH" -> "library.jar") }}}
+ */
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 ".
@@ -48,7 +93,11 @@ trait ProcessCreation {
}*/
}
- /** create ProcessBuilder with working dir optionally set to File and extra environment variables */
+ /** Create a [[scala.sys.process.ProcessBuilder]] with working dir optionally set to
+ * `File` and extra environment variables.
+ *
+ * @example {{{ apply("java" :: javaArgs, params.get("cwd"), "CLASSPATH" -> "library.jar") }}}
+ */
def apply(command: Seq[String], cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = {
val jpb = new JProcessBuilder(command.toArray: _*)
cwd foreach (jpb directory _)
@@ -56,30 +105,111 @@ trait ProcessCreation {
apply(jpb)
}
+ /** create a [[scala.sys.process.ProcessBuilder]] from a `java.lang.ProcessBuilder`.
+ *
+ * @example {{{
+ * apply((new java.lang.ProcessBuilder("ls", "-l")) directory new java.io.File(System.getProperty("user.home")))
+ * }}}
+ */
def apply(builder: JProcessBuilder): ProcessBuilder = new Simple(builder)
+
+ /** create a [[scala.sys.process.ProcessBuilder]] from a `java.io.File`. This
+ * `ProcessBuilder` can then be used as a `Source` or a `Sink`, so one can
+ * pipe things from and to it.
+ */
def apply(file: File): FileBuilder = new FileImpl(file)
+
+ /** Create a [[scala.sys.process.ProcessBuilder]] from a `java.net.URL`. This
+ * `ProcessBuilder` can then be used as a `Source`, so that one can pipe things
+ * from it.
+ */
def apply(url: URL): URLBuilder = new URLImpl(url)
+
+ /** Create a [[scala.sys.process.ProcessBuilder]] from a Scala XML Element.
+ * This can be used as a way to template strings.
+ *
+ * @example {{{
+ * apply( {dxPath.absolutePath} --dex --output={classesDexPath.absolutePath} {classesMinJarPath.absolutePath})
+ * }}}
+ */
def apply(command: scala.xml.Elem): ProcessBuilder = apply(command.text.trim)
+
+ /** Create a [[scala.sys.process.ProcessBuilder]] from a `Boolean`. This can be
+ * to force an exit value.
+ */
def apply(value: Boolean): ProcessBuilder = apply(value.toString, if (value) 0 else 1)
+ /** Create a [[scala.sys.process.ProcessBuilder]] from a `String` name and a
+ * `Boolean`. This can be used to force an exit value, with the name being
+ * used for `toString`.
+ */
def apply(name: String, exitValue: => Int): ProcessBuilder = new Dummy(name, exitValue)
+
+ /** Create a sequence of [[scala.sys.process.ProcessBuilder.Source]] from a sequence of
+ * something else for which there's an implicit conversion to `Source`.
+ */
def applySeq[T](builders: Seq[T])(implicit convert: T => Source): Seq[Source] = builders.map(convert)
+ /** Create a [[scala.sys.process.ProcessBuilder]] from one or more
+ * [[scala.sys.process.ProcessBuilder.Source]], which can then be
+ * piped to something else.
+ *
+ * This will concatenate the output of all sources.
+ */
def cat(file: Source, files: Source*): ProcessBuilder = cat(file +: files)
+
+ /** Create a [[scala.sys.process.ProcessBuilder]] from a non-empty sequence
+ * of [[scala.sys.process.ProcessBuilder.Source]], which can then be
+ * piped to something else.
+ *
+ * This will concatenate the output of all sources. For example:
+ *
+ * {{{
+ * import scala.sys.process._
+ * import scala.sys.process.Process.cat
+ * import java.net.URL
+ * import java.io.File
+ *
+ * val spde = new URL("http://technically.us/spde/About")
+ * val dispatch = new URL("http://databinder.net/dispatch/About")
+ * val build = new File("project/build.properties")
+ * cat(spde, dispatch, build) #| "grep -i scala" !
+ * }}}
+ */
def cat(files: Seq[Source]): ProcessBuilder = {
require(files.nonEmpty)
files map (_.cat) reduceLeft (_ #&& _)
}
}
+/** Provide implicit conversions for the factories offered by [[scala.sys.process.Process]]'s
+ * companion object. These implicits can then be used to decrease the noise in a pipeline
+ * of commands, making it look more shell-like. They are available through the package object
+ * [[scala.sys.process]].
+ */
trait ProcessImplicits {
import Process._
+ /** Return a sequence of [[scala.sys.process.ProcessBuilder.Source]] from a sequence
+ * of values for which an implicit conversion to `Source` is available.
+ */
implicit def buildersToProcess[T](builders: Seq[T])(implicit convert: T => Source): Seq[Source] = applySeq(builders)
+
+ /** Implicitly convert a `java.lang.ProcessBuilder` into a Scala one. */
implicit def builderToProcess(builder: JProcessBuilder): ProcessBuilder = apply(builder)
+
+ /** Implicitly convert a `java.io.File` into a [[scala.sys.process.ProcessBuilder]] */
implicit def fileToProcess(file: File): FileBuilder = apply(file)
+
+ /** Implicitly convert a `java.net.URL` into a [[scala.sys.process.ProcessBuilder]] */
implicit def urlToProcess(url: URL): URLBuilder = apply(url)
+
+ /** Implicitly convert a [[scala.xml.Elem]] into a [[scala.sys.process.ProcessBuilder]] */
implicit def xmlToProcess(command: scala.xml.Elem): ProcessBuilder = apply(command)
+
+ /** Implicitly convert a `String` into a [[scala.sys.process.ProcessBuilder]] */
implicit def stringToProcess(command: String): ProcessBuilder = apply(command)
+
+ /** Implicitly convert a sequence of `String` into a [[scala.sys.process.ProcessBuilder]] */
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 3462f865b..8b75adc97 100644
--- a/src/library/scala/sys/process/ProcessBuilder.scala
+++ b/src/library/scala/sys/process/ProcessBuilder.scala
@@ -12,7 +12,66 @@ package process
import processInternal._
import ProcessBuilder._
-/** Represents a runnable process. */
+/** Represents a runnable process.
+ *
+ * This is the main component of this package. A `ProcessBuilder` may be composed with
+ * others, either concatenating their outputs or piping them from one to the next, and
+ * possibly with conditional execution depending on the last process exit value.
+ *
+ * Once executed, one can retrieve the output or redirect it to a
+ * [[scala.sys.process.ProcessLogger]], or one can get the exit value, discarding or
+ * redirecting the output.
+ *
+ * One creates a `ProcessBuilder` through factories provided in [[scala.sys.process.Process]]'s
+ * companion object, or implicit conversions based on these factories made available in the
+ * package object [[scala.sys.process]].
+ *
+ * Let's examine in detail one example of usage:
+ *
+ * {{{
+ * import scala.sys.process._
+ * "find src -name *.scala -exec grep null {} ;" #| "xargs test -z" #&& "echo null-free" #|| "echo null detected" !
+ * }}}
+ *
+ * Note that every `String` is implicitly converted into a `ProcessBuilder`
+ * through the implicits imported from [[scala.sys.process]]. These `ProcessBuilder` are then
+ * combined in three different ways.
+ *
+ * 1. `#|` pipes the output of the first command into the input of the second command. It
+ * mirrors a shell pipe (`|`).
+ * 2. `#&&` conditionally executes the second command if the previous one finished with
+ * exit value 0. It mirrors shell's `&&`.
+ * 3. `#||` conditionally executes the third command if the exit value of the previous
+ * command is is different than zero. It mirrors shell's `&&`.
+ *
+ * Not shown here, the equivalent of a shell's `;` would be `###`. The reason for this name is
+ * that `;` is a reserved token in Scala.
+ *
+ * Finally, `!` at the end executes the commands, and returns the exit value. If the output
+ * was desired instead, one could run that with `!!` instead.
+ *
+ * If one wishes to execute the commands in background, one can either call `run`, which
+ * returns a [[scala.sys.process.Process]] from which the exit value can be obtained, or
+ * `lines`, which returns a [scala.collection.immutable.Stream] of output lines. This throws
+ * an exception at the end of the `Stream` is the exit value is non-zero. To avoid exceptions,
+ * one can use `lines_!` instead.
+ *
+ * One can also start the commands in specific ways to further control their I/O. Using `!<` to
+ * start the commands will use the stdin from the current process for them. All methods can
+ * be used passing a [[scala.sys.process.ProcessLogger]] to capture the output, both stderr and
+ * stdout. And, when using `run`, one can pass a [[scala.sys.process.ProcessIO]] to control
+ * stdin, stdout and stderr.
+ *
+ * The stdin of a command can be redirected from a `java.io.InputStream`, a `java.io.File`, a
+ * `java.net.URL` or another `ProcessBuilder` through the method `#<`. Likewise, the stdout
+ * can be sent to a `java.io.OutputStream`, a `java.io.File` or another `ProcessBuilder` with
+ * the method `#>`. The method `#>>` can be used to append the output to a `java.io.File`.
+ * For example:
+ *
+ * {{{
+ * new URL("http://databinder.net/dispatch/About") #> "grep JSON" #>> new File("About_JSON") !
+ * }}}
+ */
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.*/
@@ -83,40 +142,75 @@ trait ProcessBuilder extends Source with Sink {
def hasExitValue: Boolean
}
+/** This object contains traits used to describe input and output sources. */
object ProcessBuilder extends ProcessBuilderImpl {
+ /** Used when creating [[scala.sys.process.ProcessBuilder.Source]] from an URL. */
trait URLBuilder extends Source {
}
+
+ /** Used when creating [[scala.sys.process.ProcessBuilder.Source]] and/or
+ * [[scala.sys.process.ProcessBuilder.Sink]] from a file.
+ */
trait FileBuilder extends Sink with Source {
+ /** Append the contents of a `java.io.File` to this file */
def #<<(f: File): ProcessBuilder
+
+ /** Append the contents from a `java.net.URL` to this file */
def #<<(u: URL): ProcessBuilder
+
+ /** Append the contents of a `java.io.InputStream` to this file */
def #<<(i: => InputStream): ProcessBuilder
+
+ /** Append the contents of a [[scala.sys.process.ProcessBuilder]] to this file */
def #<<(p: ProcessBuilder): ProcessBuilder
}
+
+ /** Represents everything that can be used as an input to a
+ * [[scala.sys.process.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. */
+ * 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, "