Highlight build failures in soong output
Summary: When build failures occur, it can be hard to find where the error message begins. We now highlight "FAILURE" in red to make it easier to see what exactly has failed in the output. Test: execute automated tests with m nothing --no-skip-soong-tests, can also manually test with a build failure and observe red text in the output Change-Id: Iad3b94cc1c385f0afdebdf12c44843db04ff2bdc Signed-off-by: Cherokee Toole <cherokee@meta.com>
This commit is contained in:
committed by
Cherokee Toole
parent
e7485b871a
commit
f7119b57ac
@@ -23,26 +23,28 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type formatter struct {
|
type formatter struct {
|
||||||
format string
|
colorize bool
|
||||||
quiet bool
|
format string
|
||||||
start time.Time
|
quiet bool
|
||||||
|
start time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFormatter returns a formatter for formatting output to
|
// newFormatter returns a formatter for formatting output to
|
||||||
// the terminal in a format similar to Ninja.
|
// the terminal in a format similar to Ninja.
|
||||||
// format takes nearly all the same options as NINJA_STATUS.
|
// format takes nearly all the same options as NINJA_STATUS.
|
||||||
// %c is currently unsupported.
|
// %c is currently unsupported.
|
||||||
func newFormatter(format string, quiet bool) formatter {
|
func newFormatter(colorize bool, format string, quiet bool) formatter {
|
||||||
return formatter{
|
return formatter{
|
||||||
format: format,
|
colorize: colorize,
|
||||||
quiet: quiet,
|
format: format,
|
||||||
start: time.Now(),
|
quiet: quiet,
|
||||||
|
start: time.Now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s formatter) message(level status.MsgLevel, message string) string {
|
func (s formatter) message(level status.MsgLevel, message string) string {
|
||||||
if level >= status.ErrorLvl {
|
if level >= status.ErrorLvl {
|
||||||
return fmt.Sprintf("FAILED: %s", message)
|
return fmt.Sprintf("%s %s", s.failedString(), message)
|
||||||
} else if level > status.StatusLvl {
|
} else if level > status.StatusLvl {
|
||||||
return fmt.Sprintf("%s%s", level.Prefix(), message)
|
return fmt.Sprintf("%s%s", level.Prefix(), message)
|
||||||
} else if level == status.StatusLvl {
|
} else if level == status.StatusLvl {
|
||||||
@@ -127,9 +129,9 @@ func (s formatter) result(result status.ActionResult) string {
|
|||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
targets := strings.Join(result.Outputs, " ")
|
targets := strings.Join(result.Outputs, " ")
|
||||||
if s.quiet || result.Command == "" {
|
if s.quiet || result.Command == "" {
|
||||||
ret = fmt.Sprintf("FAILED: %s\n%s", targets, result.Output)
|
ret = fmt.Sprintf("%s %s\n%s", s.failedString(), targets, result.Output)
|
||||||
} else {
|
} else {
|
||||||
ret = fmt.Sprintf("FAILED: %s\n%s\n%s", targets, result.Command, result.Output)
|
ret = fmt.Sprintf("%s %s\n%s\n%s", s.failedString(), targets, result.Command, result.Output)
|
||||||
}
|
}
|
||||||
} else if result.Output != "" {
|
} else if result.Output != "" {
|
||||||
ret = result.Output
|
ret = result.Output
|
||||||
@@ -141,3 +143,11 @@ func (s formatter) result(result status.ActionResult) string {
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s formatter) failedString() string {
|
||||||
|
failed := "FAILED:"
|
||||||
|
if s.colorize {
|
||||||
|
failed = ansi.red() + ansi.bold() + failed + ansi.regular()
|
||||||
|
}
|
||||||
|
return failed
|
||||||
|
}
|
||||||
|
@@ -27,9 +27,10 @@ import (
|
|||||||
// statusFormat takes nearly all the same options as NINJA_STATUS.
|
// statusFormat takes nearly all the same options as NINJA_STATUS.
|
||||||
// %c is currently unsupported.
|
// %c is currently unsupported.
|
||||||
func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput {
|
func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput {
|
||||||
formatter := newFormatter(statusFormat, quietBuild)
|
canUseSmartFormatting := !forceSimpleOutput && isSmartTerminal(w)
|
||||||
|
formatter := newFormatter(canUseSmartFormatting, statusFormat, quietBuild)
|
||||||
|
|
||||||
if !forceSimpleOutput && isSmartTerminal(w) {
|
if canUseSmartFormatting {
|
||||||
return NewSmartStatusOutput(w, formatter)
|
return NewSmartStatusOutput(w, formatter)
|
||||||
} else {
|
} else {
|
||||||
return NewSimpleStatusOutput(w, formatter, forceKeepANSI)
|
return NewSimpleStatusOutput(w, formatter, forceKeepANSI)
|
||||||
|
@@ -58,7 +58,7 @@ func TestStatusOutput(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "action with error",
|
name: "action with error",
|
||||||
calls: actionsWithError,
|
calls: actionsWithError,
|
||||||
smart: "\r\x1b[1m[ 0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
|
smart: "\r\x1b[1m[ 0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m f1 f2\ntouch f1 f2\nerror1\nerror2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
|
||||||
simple: "[ 33% 1/3] action1\n[ 66% 2/3] action2\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n[100% 3/3] action3\n",
|
simple: "[ 33% 1/3] action1\n[ 66% 2/3] action2\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n[100% 3/3] action3\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -70,7 +70,7 @@ func TestStatusOutput(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "messages",
|
name: "messages",
|
||||||
calls: actionsWithMessages,
|
calls: actionsWithMessages,
|
||||||
smart: "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1mstatus\x1b[0m\x1b[K\r\x1b[Kprint\nFAILED: error\n\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
|
smart: "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1mstatus\x1b[0m\x1b[K\r\x1b[Kprint\n\x1b[31m\x1b[1mFAILED:\x1b[0m error\n\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
|
||||||
simple: "[ 50% 1/2] action1\nstatus\nprint\nFAILED: error\n[100% 2/2] action2\n",
|
simple: "[ 50% 1/2] action1\nstatus\nprint\nFAILED: error\n[100% 2/2] action2\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -362,7 +362,7 @@ func TestSmartStatusHideAfterFailure(t *testing.T) {
|
|||||||
|
|
||||||
stat.Flush()
|
stat.Flush()
|
||||||
|
|
||||||
w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nThere was 1 action that completed after the action that failed. See verbose.log.gz for its output.\n"
|
w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nThere was 1 action that completed after the action that failed. See verbose.log.gz for its output.\n"
|
||||||
|
|
||||||
if g := smart.String(); g != w {
|
if g := smart.String(); g != w {
|
||||||
t.Errorf("want:\n%q\ngot:\n%q", w, g)
|
t.Errorf("want:\n%q\ngot:\n%q", w, g)
|
||||||
@@ -407,7 +407,7 @@ func TestSmartStatusHideAfterFailurePlural(t *testing.T) {
|
|||||||
|
|
||||||
stat.Flush()
|
stat.Flush()
|
||||||
|
|
||||||
w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action3\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\r\x1b[1m[150% 3/2] action3\x1b[0m\x1b[K\nThere were 2 actions that completed after the action that failed. See verbose.log.gz for their output.\n"
|
w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action3\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\r\x1b[1m[150% 3/2] action3\x1b[0m\x1b[K\nThere were 2 actions that completed after the action that failed. See verbose.log.gz for their output.\n"
|
||||||
|
|
||||||
if g := smart.String(); g != w {
|
if g := smart.String(); g != w {
|
||||||
t.Errorf("want:\n%q\ngot:\n%q", w, g)
|
t.Errorf("want:\n%q\ngot:\n%q", w, g)
|
||||||
@@ -445,7 +445,7 @@ func TestSmartStatusDontHideErrorAfterFailure(t *testing.T) {
|
|||||||
|
|
||||||
stat.Flush()
|
stat.Flush()
|
||||||
|
|
||||||
w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nFAILED: \nOutput2\n"
|
w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput2\n"
|
||||||
|
|
||||||
if g := smart.String(); g != w {
|
if g := smart.String(); g != w {
|
||||||
t.Errorf("want:\n%q\ngot:\n%q", w, g)
|
t.Errorf("want:\n%q\ngot:\n%q", w, g)
|
||||||
|
Reference in New Issue
Block a user