summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJosh Suereth <joshua.suereth@gmail.com>2011-08-31 19:16:18 +0000
committerJosh Suereth <joshua.suereth@gmail.com>2011-08-31 19:16:18 +0000
commit0377cad8c6a26be5b6dd59323dd171634400005a (patch)
treec0834284a990bd94c76d06d28785d9b84a9a4e41 /src
parent0e0e2055f355bf701d9a712abf69dcb52fc1eda1 (diff)
downloadscala-0377cad8c6a26be5b6dd59323dd171634400005a.tar.gz
scala-0377cad8c6a26be5b6dd59323dd171634400005a.tar.bz2
scala-0377cad8c6a26be5b6dd59323dd171634400005a.zip
Document the usage and methods of scala.sys.pro...
Document the usage and methods of scala.sys.process. From the first international scaladoc marathon. Contributed by: Daniel Sobral
Diffstat (limited to 'src')
-rw-r--r--src/library/scala/sys/process/BasicIO.scala6
-rw-r--r--src/library/scala/sys/process/Process.scala138
-rw-r--r--src/library/scala/sys/process/ProcessBuilder.scala104
-rw-r--r--src/library/scala/sys/process/ProcessIO.scala7
-rw-r--r--src/library/scala/sys/process/ProcessLogger.scala18
-rw-r--r--src/library/scala/sys/process/package.scala52
6 files changed, 315 insertions, 10 deletions
diff --git a/src/library/scala/sys/process/BasicIO.scala b/src/library/scala/sys/process/BasicIO.scala
index 6adce06f36..44e573896f 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 01fbaf19e3..db10ff9c1d 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(<x> {dxPath.absolutePath} --dex --output={classesDexPath.absolutePath} {classesMinJarPath.absolutePath}</x>)
+ * }}}
+ */
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 0e34e7be1b..9acdc59094 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, "<output stream>"))
+
+ /** Writes the output stream of this process to a [[scala.sys.process.ProcessBuilder]]. */
def #>(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(toSource, b, false)
+
+ /** Returnes a [[scala.sys.process.ProcessBuilder]] representing this `Source`. */
def cat = toSource
private def toFile(f: File, append: Boolean) = #> (new FileOutput(f, append))
}
+
+ /** Represents everything that can receive an output from a
+ * [[scala.sys.process.ProcessBuilder]].
+ */
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. */
+ * 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, "<input stream>"))
+
+ /** Reads the output of a [[scala.sys.process.ProcessBuilder]] into the input stream of this process. */
def #<(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(b, toSink, false)
}
}
diff --git a/src/library/scala/sys/process/ProcessIO.scala b/src/library/scala/sys/process/ProcessIO.scala
index 041cad1ef7..261e837a4d 100644
--- a/src/library/scala/sys/process/ProcessIO.scala
+++ b/src/library/scala/sys/process/ProcessIO.scala
@@ -11,7 +11,12 @@ package process
import processInternal._
-/** Each method will be called in a separate thread.
+/** This class is used to control the I/O of every [[scala.sys.process.ProcessBuilder]].
+ * Most of the time, there is no need to interact with `ProcessIO` directly. However, if
+ * fine control over the I/O of a `ProcessBuilder` is desired, one can use the factories
+ * on [[scala.sys.process.BasicIO]] stand-alone object to create one.
+ *
+ * Each method will be called in a separate thread.
* If daemonizeThreads is true, they will all be marked daemon threads.
*/
final class ProcessIO(
diff --git a/src/library/scala/sys/process/ProcessLogger.scala b/src/library/scala/sys/process/ProcessLogger.scala
index 556d9ad922..67146dd70e 100644
--- a/src/library/scala/sys/process/ProcessLogger.scala
+++ b/src/library/scala/sys/process/ProcessLogger.scala
@@ -39,6 +39,7 @@ trait ProcessLogger {
def buffer[T](f: => T): T
}
+/** A [[scala.sys.process.ProcessLogger]] that writes output to a file. */
class FileProcessLogger(file: File) extends ProcessLogger with Closeable with Flushable {
private val writer = (
new PrintWriter(
@@ -56,9 +57,26 @@ class FileProcessLogger(file: File) extends ProcessLogger with Closeable with Fl
def flush(): Unit = writer.flush()
}
+/** Provides factories to create [[scala.sys.process.ProcessLogger]], which
+ * are used to capture output of [[scala.sys.process.ProcessBuilder]] commands
+ * when run.
+ */
object ProcessLogger {
+ /** Creates a [[scala.sys.process.ProcessLogger]] that redirects output to a `java.io.File`. */
def apply(file: File): FileProcessLogger = new FileProcessLogger(file)
+
+ /** Creates a [[scala.sys.process.ProcessLogger]] that sends all output, standard and error,
+ * to the passed function.
+ */
def apply(fn: String => Unit): ProcessLogger = apply(fn, fn)
+
+ /** Creates a [[scala.sys.process.ProcessLogger]] that sends all output to the corresponding
+ * function.
+ *
+ * @param fout This function will receive standard outpout.
+ *
+ * @param ferr This function will receive standard error.
+ */
def apply(fout: String => Unit, ferr: String => Unit): ProcessLogger =
new ProcessLogger {
def out(s: => String): Unit = fout(s)
diff --git a/src/library/scala/sys/process/package.scala b/src/library/scala/sys/process/package.scala
index 51368e8e4c..17636c4c87 100644
--- a/src/library/scala/sys/process/package.scala
+++ b/src/library/scala/sys/process/package.scala
@@ -11,6 +11,58 @@
// for process debugging output.
//
package scala.sys {
+ /**
+ * This package is used to create process pipelines, similar to Unix command pipelines.
+ *
+ * The key concept is that one builds a [[scala.sys.process.Process]] that will run and return an exit
+ * value. This `Process` is usually composed of one or more [[scala.sys.process.ProcessBuilder]], fed by a
+ * [[scala.sys.process.ProcessBuilder.Source]] and feeding a [[scala.sys.process.ProcessBuilder.Sink]]. A
+ * `ProcessBuilder` itself is both a `Source` and a `Sink`.
+ *
+ * As `ProcessBuilder`, `Sink` and `Source` are abstract, one usually creates them with `apply` methods on
+ * the companion object of [[scala.sys.process.Process]], or through implicit conversions available in this
+ * package object from `String` and other types. The pipe is composed through unix-like pipeline and I/O
+ * redirection operators available on [[scala.sys.process.ProcessBuilder]].
+ *
+ * The example below shows how to build and combine such commands. It searches for `null` uses in the `src`
+ * directory, printing a message indicating whether they were found or not. The first command pipes its
+ * output to the second command, whose exit value is then used to choose between the third or fourth
+ * commands. This same example is explained in greater detail on [[scala.sys.process.ProcessBuilder]].
+ *
+ * {{{
+ * import scala.sys.process._
+ * (
+ * "find src -name *.scala -exec grep null {} ;"
+ * #| "xargs test -z"
+ * #&& "echo null-free" #|| "echo null detected"
+ * ) !
+ * }}}
+ *
+ * Other implicits available here are for [[scala.sys.process.ProcessBuilder.FileBuilder]], which extends
+ * both `Sink` and `Source`, and for [[scala.sys.process.ProcessBuilder.URLBuilder]], which extends
+ * `Source` alone.
+ *
+ * One can even create a `Process` solely out of these, without running any command. For example, this will
+ * download from a URL to a file:
+ *
+ * {{{
+ * import java.io.File
+ * import scala.sys.process._
+ * new File("About.html") #> new File("About_copy.html") !
+ * }}}
+ *
+ * One may use a `Process` directly through `ProcessBuilder`'s `run` method, which starts the process in
+ * the background, and returns a `Process`. If background execution is not desired, one can get a
+ * `ProcessBuilder` to execute through a method such as `!`, `lines`, `run` or variations thereof. That
+ * will create the `Process` to execute the commands, and return either the exit value or the output, maybe
+ * throwing an exception.
+ *
+ * Finally, when executing a `ProcessBuilder`, one may pass a [[scala.sys.process.ProcessLogger]] to
+ * capture stdout and stderr of the executing processes. A `ProcessLogger` may be created through its
+ * companion object from functions of type `(String) => Unit`, or one might redirect it to a file, using
+ * [[scala.sys.process.FileProcessLogger]], which can also be created through `ProcessLogger`'s object
+ * companion.
+ */
package object process extends ProcessImplicits {
def javaVmArguments: List[String] = {
import collection.JavaConversions._