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