mirror of
https://github.com/sbt/sbt.git
synced 2026-08-22 06:07:24 +02:00
Use java 7 Redirect.INHERIT to inherit subprocess' input stream.
This commit is contained in:
committed by
Mark Harrah
parent
53ee1c5adf
commit
8d123081a2
Executable
+20
@@ -0,0 +1,20 @@
|
|||||||
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2012 Eugene Vigdorchik
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import java.lang.{ProcessBuilder => JProcessBuilder}
|
||||||
|
|
||||||
|
/** On java 7, inherit System.in for a ProcessBuilder. */
|
||||||
|
private[sbt] object InheritInput {
|
||||||
|
def apply(p: JProcessBuilder): (Boolean, JProcessBuilder) = (redirectInput, inherit) match {
|
||||||
|
case (Some(m), Some(f)) => (true, m.invoke(p, f).asInstanceOf[JProcessBuilder])
|
||||||
|
case _ => (false, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
private[this] val pbClass = Class.forName("java.lang.ProcessBuilder")
|
||||||
|
private[this] val redirectClass = pbClass.getClasses find (_.getSimpleName == "Redirect")
|
||||||
|
|
||||||
|
private[this] val redirectInput = redirectClass map (pbClass.getMethod("redirectInput", _))
|
||||||
|
private[this] val inherit = redirectClass map (_ getField "INHERIT" get null)
|
||||||
|
}
|
||||||
@@ -379,40 +379,41 @@ private[sbt] class SimpleProcessBuilder(p: JProcessBuilder) extends AbstractProc
|
|||||||
{
|
{
|
||||||
override def run(io: ProcessIO): Process =
|
override def run(io: ProcessIO): Process =
|
||||||
{
|
{
|
||||||
val process = p.start() // start the external process
|
val (inherited, pp) = InheritInput(p)
|
||||||
|
val process = pp.start() // start the external process
|
||||||
import io.{writeInput, processOutput, processError}
|
import io.{writeInput, processOutput, processError}
|
||||||
// spawn threads that process the input, output, and error streams using the functions defined in `io`
|
// spawn threads that process the input, output, and error streams using the functions defined in `io`
|
||||||
val inThread = Spawn(writeInput(process.getOutputStream), true)
|
if(!inherited)
|
||||||
|
Spawn(writeInput(process.getOutputStream), true)
|
||||||
|
|
||||||
val outThread = Spawn(processOutput(process.getInputStream))
|
val outThread = Spawn(processOutput(process.getInputStream))
|
||||||
val errorThread =
|
val errorThread =
|
||||||
if(!p.redirectErrorStream)
|
if(!p.redirectErrorStream)
|
||||||
Spawn(processError(process.getErrorStream)) :: Nil
|
Spawn(processError(process.getErrorStream)) :: Nil
|
||||||
else
|
else
|
||||||
Nil
|
Nil
|
||||||
new SimpleProcess(process, inThread, outThread :: errorThread)
|
new SimpleProcess(process, outThread :: errorThread)
|
||||||
}
|
}
|
||||||
override def toString = p.command.toString
|
override def toString = p.command.toString
|
||||||
override def canPipeTo = true
|
override def canPipeTo = true
|
||||||
}
|
}
|
||||||
/** 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
|
/** A thin wrapper around a java.lang.Process. `outputThreads` are the Threads created to read from the
|
||||||
* the process.
|
* output and error streams of the process.
|
||||||
* The implementation of `exitValue` interrupts `inputThread` and then waits until all I/O threads die before
|
* The implementation of `exitValue` wait for the process to finish and then waits until the threads reading output and error streams die before
|
||||||
* returning. */
|
* returning. Note that the thread that reads the input stream cannot be interrupted, see https://github.com/harrah/xsbt/issues/327 and
|
||||||
private class SimpleProcess(p: JProcess, inputThread: Thread, outputThreads: List[Thread]) extends Process
|
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4514257 */
|
||||||
|
private class SimpleProcess(p: JProcess, outputThreads: List[Thread]) extends Process
|
||||||
{
|
{
|
||||||
override def exitValue() =
|
override def exitValue() =
|
||||||
{
|
{
|
||||||
try { p.waitFor() }// wait for the process to terminate
|
def waitDone(): Unit =
|
||||||
finally { inputThread.interrupt() } // we interrupt the input thread to notify it that it can terminate
|
try { p.waitFor() } catch { case _: InterruptedException => waitDone() }
|
||||||
|
waitDone()
|
||||||
outputThreads.foreach(_.join()) // this ensures that all output is complete before returning (waitFor does not ensure this)
|
outputThreads.foreach(_.join()) // this ensures that all output is complete before returning (waitFor does not ensure this)
|
||||||
p.exitValue()
|
p.exitValue()
|
||||||
}
|
}
|
||||||
override def destroy() =
|
override def destroy() = p.destroy()
|
||||||
{
|
|
||||||
try { p.destroy() }
|
|
||||||
finally { inputThread.interrupt() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FileOutput(file: File, append: Boolean) extends OutputStreamBuilder(new FileOutputStream(file, append), file.getAbsolutePath)
|
private class FileOutput(file: File, append: Boolean) extends OutputStreamBuilder(new FileOutputStream(file, append), file.getAbsolutePath)
|
||||||
|
|||||||
Reference in New Issue
Block a user