| // Copyright 2019 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "fmt" |
| "os/exec" |
| "runtime" |
| "strings" |
| "syscall" |
| ) |
| |
| type userError struct { |
| err string |
| } |
| |
| var _ error = userError{} |
| |
| func (err userError) Error() string { |
| return err.err |
| } |
| |
| func newUserErrorf(format string, v ...interface{}) userError { |
| return userError{err: fmt.Sprintf(format, v...)} |
| } |
| |
| func newErrorwithSourceLocf(format string, v ...interface{}) error { |
| return newErrorwithSourceLocfInternal(2, format, v...) |
| } |
| |
| func wrapErrorwithSourceLocf(err error, format string, v ...interface{}) error { |
| 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) |
| if !ok { |
| file = "???" |
| line = 0 |
| } |
| if lastSlash := strings.LastIndex(file, "/"); lastSlash >= 0 { |
| file = file[lastSlash+1:] |
| } |
| |
| return fmt.Errorf("%s:%d: %s", file, line, fmt.Sprintf(format, v...)) |
| } |
| |
| func getExitCode(err error) (exitCode int, ok bool) { |
| if err == nil { |
| return 0, true |
| } |
| if exiterr, ok := err.(*exec.ExitError); ok { |
| if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { |
| return status.ExitStatus(), true |
| } |
| } |
| 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 |
| } |