• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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