Unify command error handling

BUG=chromium:773875
TEST=unit test

Change-Id: Ibe32309c021d72e08cecc7d6830756fa1503e809
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/1695801
Reviewed-by: Tobias Bosch <tbosch@google.com>
Tested-by: Tobias Bosch <tbosch@google.com>
diff --git a/compiler_wrapper/errors.go b/compiler_wrapper/errors.go
index 525b986..7095e37 100644
--- a/compiler_wrapper/errors.go
+++ b/compiler_wrapper/errors.go
@@ -30,6 +30,20 @@
 	return newErrorwithSourceLocfInternal(2, "%s: %s", fmt.Sprintf(format, v...), err.Error())
 }
 
+func wrapSubprocessErrorWithSourceLoc(cmd *command, subprocessErr error) (exitCode int, err error) {
+	if subprocessErr == nil {
+		return 0, nil
+	}
+	if userErr, ok := getCCacheError(cmd, subprocessErr); ok {
+		return 0, userErr
+	}
+	if exitCode, ok := getExitCode(subprocessErr); ok {
+		return exitCode, nil
+	}
+	err = newErrorwithSourceLocfInternal(2, "failed to execute %#v: %s", cmd, subprocessErr)
+	return 0, err
+}
+
 // Based on the implementation of log.Output
 func newErrorwithSourceLocfInternal(skip int, format string, v ...interface{}) error {
 	_, file, line, ok := runtime.Caller(skip)
@@ -55,3 +69,14 @@
 	}
 	return 0, false
 }
+
+func getCCacheError(compilerCmd *command, compilerCmdErr error) (ccacheErr userError, ok bool) {
+	if en, ok := compilerCmdErr.(syscall.Errno); ok && en == syscall.ENOENT &&
+		strings.Contains(compilerCmd.path, "ccache") {
+		ccacheErr =
+			newUserErrorf("ccache not found under %s. Please install it",
+				compilerCmd.path)
+		return ccacheErr, true
+	}
+	return ccacheErr, false
+}