-
-
Notifications
You must be signed in to change notification settings - Fork 358
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Automatically pump
os.proc
streams when SystemStreams
are redirec…
…ted (#3275) Depends on com-lihaoyi/os-lib#283 This moves the subprocess stream handling logic out of `Jvm.spawnSubprocess` and makes it apply to all `os.proc` invocations, greatly reducing the room for error. With this, `Jvm.spawnSubprocess` becomes a very thin wrapper around `os.proc.spawn`. We also rely directly on OS-Lib's own pumper threads to pump to our destination, rather than having them pump into in-memory buffers and then spawning our own pumper threads to pump from those buffers to the destination I spent some time looking into how to do the stdout/err handling at the process level, but couldn't find any reasonable mechanism to do so that allows us to preserve the ordering of the stdout/stderr. This is the original motivation to squishing it into one stream via `ProxyOutputStream`/`ProxyStreamPumper` and is important because otherwise you find e.g. stack traces out of order with printlns, which makes debugging very difficult. Might be possible using some socket/fifo/pipe cleverness, but not as part of this PR Added an integration test to assert on the subtleties of stdout, stderr, and their inherited alternatives. This PR is required for the test to pass
- Loading branch information
Showing
11 changed files
with
249 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import mill._ | ||
|
||
|
||
def inheritInterleaved = T { | ||
for (i <- Range.inclusive(1, 9)) { | ||
println("print stdout" + i) | ||
os.proc("echo", "proc stdout" + i).call(stdout = os.Inherit) | ||
System.err.println("print stderr" + i) | ||
os.proc("bash", "-c", s"echo proc stderr${i} >&2").call(stderr = os.Inherit) | ||
} | ||
} | ||
|
||
def inheritRaw = T{ | ||
println("print stdoutRaw") | ||
os.proc("echo", "proc stdoutRaw").call(stdout = os.InheritRaw) | ||
System.err.println("print stderrRaw") | ||
os.proc("bash", "-c", "echo proc stderrRaw >&2").call(stderr = os.InheritRaw) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
#!/usr/bin/env sh | ||
|
||
# This is a wrapper script, that automatically download mill from GitHub release pages | ||
# You can give the required mill version with MILL_VERSION env variable | ||
# If no version is given, it falls back to the value of DEFAULT_MILL_VERSION | ||
|
||
set -e | ||
|
||
if [ -z "${DEFAULT_MILL_VERSION}" ] ; then | ||
DEFAULT_MILL_VERSION=0.11.6 | ||
fi | ||
|
||
if [ -z "$MILL_VERSION" ] ; then | ||
if [ -f ".mill-version" ] ; then | ||
MILL_VERSION="$(head -n 1 .mill-version 2> /dev/null)" | ||
elif [ -f ".config/mill-version" ] ; then | ||
MILL_VERSION="$(head -n 1 .config/mill-version 2> /dev/null)" | ||
elif [ -f "mill" ] && [ "$0" != "mill" ] ; then | ||
MILL_VERSION=$(grep -F "DEFAULT_MILL_VERSION=" "mill" | head -n 1 | cut -d= -f2) | ||
else | ||
MILL_VERSION=$DEFAULT_MILL_VERSION | ||
fi | ||
fi | ||
|
||
if [ "x${XDG_CACHE_HOME}" != "x" ] ; then | ||
MILL_DOWNLOAD_PATH="${XDG_CACHE_HOME}/mill/download" | ||
else | ||
MILL_DOWNLOAD_PATH="${HOME}/.cache/mill/download" | ||
fi | ||
MILL_EXEC_PATH="${MILL_DOWNLOAD_PATH}/${MILL_VERSION}" | ||
|
||
version_remainder="$MILL_VERSION" | ||
MILL_MAJOR_VERSION="${version_remainder%%.*}"; version_remainder="${version_remainder#*.}" | ||
MILL_MINOR_VERSION="${version_remainder%%.*}"; version_remainder="${version_remainder#*.}" | ||
|
||
if [ ! -s "$MILL_EXEC_PATH" ] ; then | ||
mkdir -p "$MILL_DOWNLOAD_PATH" | ||
if [ "$MILL_MAJOR_VERSION" -gt 0 ] || [ "$MILL_MINOR_VERSION" -ge 5 ] ; then | ||
ASSEMBLY="-assembly" | ||
fi | ||
DOWNLOAD_FILE=$MILL_EXEC_PATH-tmp-download | ||
MILL_VERSION_TAG=$(echo $MILL_VERSION | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/') | ||
MILL_DOWNLOAD_URL="https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/$MILL_VERSION/mill-dist-$MILL_VERSION.jar" | ||
curl --fail -L -o "$DOWNLOAD_FILE" "$MILL_DOWNLOAD_URL" | ||
chmod +x "$DOWNLOAD_FILE" | ||
mv "$DOWNLOAD_FILE" "$MILL_EXEC_PATH" | ||
unset DOWNLOAD_FILE | ||
unset MILL_DOWNLOAD_URL | ||
fi | ||
|
||
if [ -z "$MILL_MAIN_CLI" ] ; then | ||
MILL_MAIN_CLI="${0}" | ||
fi | ||
|
||
MILL_FIRST_ARG="" | ||
|
||
# first arg is a long flag for "--interactive" or starts with "-i" | ||
if [ "$1" = "--bsp" ] || [ "${1#"-i"}" != "$1" ] || [ "$1" = "--interactive" ] || [ "$1" = "--no-server" ] || [ "$1" = "--repl" ] || [ "$1" = "--help" ] ; then | ||
# Need to preserve the first position of those listed options | ||
MILL_FIRST_ARG=$1 | ||
shift | ||
fi | ||
|
||
unset MILL_DOWNLOAD_PATH | ||
unset MILL_VERSION | ||
|
||
exec $MILL_EXEC_PATH $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@" |
87 changes: 87 additions & 0 deletions
87
integration/feature/subprocess-stdout/test/src/SubprocessStdoutTests.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package mill.integration | ||
|
||
import utest._ | ||
|
||
object SubprocessStdoutTests extends IntegrationTestSuite { | ||
val tests: Tests = Tests { | ||
initWorkspace() | ||
|
||
test { | ||
val res1 = evalStdCombined("inheritInterleaved").out | ||
// Make sure that when a lot of printed/inherited stdout/stderr is printed | ||
// in quick succession, the output ordering is preserved and it doesn't get | ||
// jumbled up | ||
assert( | ||
res1.contains( | ||
s"""print stdout1 | ||
|proc stdout1 | ||
|print stderr1 | ||
|proc stderr1 | ||
|print stdout2 | ||
|proc stdout2 | ||
|print stderr2 | ||
|proc stderr2 | ||
|print stdout3 | ||
|proc stdout3 | ||
|print stderr3 | ||
|proc stderr3 | ||
|print stdout4 | ||
|proc stdout4 | ||
|print stderr4 | ||
|proc stderr4 | ||
|print stdout5 | ||
|proc stdout5 | ||
|print stderr5 | ||
|proc stderr5 | ||
|print stdout6 | ||
|proc stdout6 | ||
|print stderr6 | ||
|proc stderr6 | ||
|print stdout7 | ||
|proc stdout7 | ||
|print stderr7 | ||
|proc stderr7 | ||
|print stdout8 | ||
|proc stdout8 | ||
|print stderr8 | ||
|proc stderr8 | ||
|print stdout9 | ||
|proc stdout9 | ||
|print stderr9 | ||
|proc stderr9""".stripMargin | ||
) | ||
) | ||
|
||
// Make sure subprocess output that isn't captures by all of Mill's stdout/stderr/os.Inherit | ||
// redirects still gets pikced up from the stdout/stderr log files and displayed. They may | ||
// be out of order from the original Mill stdout/stderr, but they should still at least turn | ||
// up in the console somewhere and not disappear | ||
// | ||
val res2 = evalStdCombined("inheritRaw").out | ||
if (integrationTestMode == "fork") { | ||
// For `fork` tests, which represent `-i`/`--interactive`/`--no-server`, the output should | ||
// be properly ordered since it all comes directly from the stdout/stderr of the same process | ||
assert( | ||
res2.contains( | ||
"""print stdoutRaw | ||
|proc stdoutRaw | ||
|print stderrRaw | ||
|proc stderrRaw""".stripMargin | ||
) | ||
) | ||
} else { | ||
// Note that it should be out of order, because both `print`s will be captured and logged first, | ||
// whereas the two `proc` outputs will get sent to their respective log files and only noticed | ||
// a few milliseconds later as the files are polled for updates | ||
assert( | ||
res2.contains( | ||
"""print stdoutRaw | ||
|print stderrRaw | ||
|proc stdoutRaw | ||
|proc stderrRaw""".stripMargin | ||
) | ||
) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.