compiler_wrapper: pre-buffer stdin properly.

This CL makes our compiler wrapper pre-buffer all stdin when clang is
expected to depend on it. Otherwise, clang might not read all of the
stdin it's given, so future invocations of clang will only be handed
partial stdin.

BUG=chromium:1052532
TEST=emerged the kernel; sdk tryjob

Change-Id: I6660e6333a1e8cf4649ef38de5c91d0b700fe1b5
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/2080852
Tested-by: George Burgess <gbiv@chromium.org>
Reviewed-by: Tiancong Wang <tcwang@google.com>
diff --git a/compiler_wrapper/compiler_wrapper.go b/compiler_wrapper/compiler_wrapper.go
index 11fd819..cecde1c 100644
--- a/compiler_wrapper/compiler_wrapper.go
+++ b/compiler_wrapper/compiler_wrapper.go
@@ -5,6 +5,7 @@
 package main
 
 import (
+	"bytes"
 	"fmt"
 	"io"
 	"path/filepath"
@@ -231,16 +232,30 @@
 	}
 }
 
-func teeStdinIfNeeded(env env, inputCmd *command, dest io.Writer) io.Reader {
-	// We can't use io.TeeReader unconditionally, as that would block
-	// calls to exec.Cmd.Run(), even if the underlying process has already
-	// terminated. See https://github.com/golang/go/issues/7990 for more details.
+func needStdinTee(inputCmd *command) bool {
 	lastArg := ""
 	for _, arg := range inputCmd.Args {
 		if arg == "-" && lastArg != "-o" {
-			return io.TeeReader(env.stdin(), dest)
+			return true
 		}
 		lastArg = arg
 	}
-	return env.stdin()
+	return false
+}
+
+func prebufferStdinIfNeeded(env env, inputCmd *command) (getStdin func() io.Reader, err error) {
+	// We pre-buffer the entirety of stdin, since the compiler may exit mid-invocation with an
+	// error, which may leave stdin partially read.
+	if !needStdinTee(inputCmd) {
+		// This won't produce deterministic input to the compiler, but stdin shouldn't
+		// matter in this case, so...
+		return env.stdin, nil
+	}
+
+	stdinBuffer := &bytes.Buffer{}
+	if _, err := stdinBuffer.ReadFrom(env.stdin()); err != nil {
+		return nil, wrapErrorwithSourceLocf(err, "prebuffering stdin")
+	}
+
+	return func() io.Reader { return bytes.NewReader(stdinBuffer.Bytes()) }, nil
 }