1// Copyright 2019 The Chromium OS Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package main 6 7import ( 8 "fmt" 9 "os/exec" 10 "runtime" 11 "strings" 12 "syscall" 13) 14 15type userError struct { 16 err string 17} 18 19var _ error = userError{} 20 21func (err userError) Error() string { 22 return err.err 23} 24 25func newUserErrorf(format string, v ...interface{}) userError { 26 return userError{err: fmt.Sprintf(format, v...)} 27} 28 29func newErrorwithSourceLocf(format string, v ...interface{}) error { 30 return newErrorwithSourceLocfInternal(2, format, v...) 31} 32 33func wrapErrorwithSourceLocf(err error, format string, v ...interface{}) error { 34 return newErrorwithSourceLocfInternal(2, "%s: %s", fmt.Sprintf(format, v...), err.Error()) 35} 36 37func wrapSubprocessErrorWithSourceLoc(cmd *command, subprocessErr error) (exitCode int, err error) { 38 if subprocessErr == nil { 39 return 0, nil 40 } 41 if userErr, ok := getCCacheError(cmd, subprocessErr); ok { 42 return 0, userErr 43 } 44 if exitCode, ok := getExitCode(subprocessErr); ok { 45 return exitCode, nil 46 } 47 err = newErrorwithSourceLocfInternal(2, "failed to execute %#v: %s", cmd, subprocessErr) 48 return 0, err 49} 50 51// Based on the implementation of log.Output 52func newErrorwithSourceLocfInternal(skip int, format string, v ...interface{}) error { 53 _, file, line, ok := runtime.Caller(skip) 54 if !ok { 55 file = "???" 56 line = 0 57 } 58 if lastSlash := strings.LastIndex(file, "/"); lastSlash >= 0 { 59 file = file[lastSlash+1:] 60 } 61 62 return fmt.Errorf("%s:%d: %s", file, line, fmt.Sprintf(format, v...)) 63} 64 65func getExitCode(err error) (exitCode int, ok bool) { 66 if err == nil { 67 return 0, true 68 } 69 if exiterr, ok := err.(*exec.ExitError); ok { 70 if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { 71 return status.ExitStatus(), true 72 } 73 } 74 return 0, false 75} 76 77func getCCacheError(compilerCmd *command, compilerCmdErr error) (ccacheErr userError, ok bool) { 78 if en, ok := compilerCmdErr.(syscall.Errno); ok && en == syscall.ENOENT && 79 strings.Contains(compilerCmd.Path, "ccache") { 80 ccacheErr = 81 newUserErrorf("ccache not found under %s. Please install it", 82 compilerCmd.Path) 83 return ccacheErr, true 84 } 85 return ccacheErr, false 86} 87