diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go index fa63b465d..3c9cac190 100644 --- a/cmd/multiproduct_kati/main.go +++ b/cmd/multiproduct_kati/main.go @@ -218,10 +218,16 @@ func distDir(outDir string) string { } } +func forceAnsiOutput() bool { + value := os.Getenv("SOONG_UI_ANSI_OUTPUT") + return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true" +} + func main() { stdio := terminal.StdioImpl{} - output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false) + output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false, + forceAnsiOutput()) log := logger.New(output) defer log.Cleanup() diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go index d70978727..9ee373e79 100644 --- a/cmd/soong_ui/main.go +++ b/cmd/soong_ui/main.go @@ -164,7 +164,8 @@ func main() { // Create a terminal output that mimics Ninja's. output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput, - build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")) + build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"), + build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT")) // Attach a new logger instance to the terminal output. log := logger.New(output) diff --git a/cuj/cuj.go b/cuj/cuj.go index c671f6f09..869e0f7b1 100644 --- a/cuj/cuj.go +++ b/cuj/cuj.go @@ -48,7 +48,7 @@ type TestResults struct { // Run runs a single build command. It emulates the "m" command line by calling into Soong UI directly. func (t *Test) Run(logsDir string) { - output := terminal.NewStatusOutput(os.Stdout, "", false, false) + output := terminal.NewStatusOutput(os.Stdout, "", false, false, false) log := logger.New(output) defer log.Cleanup() diff --git a/ui/terminal/simple_status.go b/ui/terminal/simple_status.go index 4e8c56804..936b275a1 100644 --- a/ui/terminal/simple_status.go +++ b/ui/terminal/simple_status.go @@ -24,15 +24,17 @@ import ( type simpleStatusOutput struct { writer io.Writer formatter formatter + keepANSI bool } // NewSimpleStatusOutput returns a StatusOutput that represents the // current build status similarly to Ninja's built-in terminal // output. -func NewSimpleStatusOutput(w io.Writer, formatter formatter) status.StatusOutput { +func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool) status.StatusOutput { return &simpleStatusOutput{ writer: w, formatter: formatter, + keepANSI: keepANSI, } } @@ -54,7 +56,9 @@ func (s *simpleStatusOutput) FinishAction(result status.ActionResult, counts sta progress := s.formatter.progress(counts) + str output := s.formatter.result(result) - output = string(stripAnsiEscapes([]byte(output))) + if !s.keepANSI { + output = string(stripAnsiEscapes([]byte(output))) + } if output != "" { fmt.Fprint(s.writer, progress, "\n", output) diff --git a/ui/terminal/smart_status.go b/ui/terminal/smart_status.go index 6bdf14074..06a4064ff 100644 --- a/ui/terminal/smart_status.go +++ b/ui/terminal/smart_status.go @@ -77,7 +77,12 @@ func NewSmartStatusOutput(w io.Writer, formatter formatter) status.StatusOutput s.requestedTableHeight = h } - s.updateTermSize() + if w, h, ok := termSize(s.writer); ok { + s.termWidth, s.termHeight = w, h + s.computeTableHeight() + } else { + s.tableMode = false + } if s.tableMode { // Add empty lines at the bottom of the screen to scroll back the existing history @@ -296,40 +301,44 @@ func (s *smartStatusOutput) stopSigwinch() { close(s.sigwinch) } +// computeTableHeight recomputes s.tableHeight based on s.termHeight and s.requestedTableHeight. +func (s *smartStatusOutput) computeTableHeight() { + tableHeight := s.requestedTableHeight + if tableHeight == 0 { + tableHeight = s.termHeight / 4 + if tableHeight < 1 { + tableHeight = 1 + } else if tableHeight > 10 { + tableHeight = 10 + } + } + if tableHeight > s.termHeight-1 { + tableHeight = s.termHeight - 1 + } + s.tableHeight = tableHeight +} + +// updateTermSize recomputes the table height after a SIGWINCH and pans any existing text if +// necessary. func (s *smartStatusOutput) updateTermSize() { if w, h, ok := termSize(s.writer); ok { - firstUpdate := s.termHeight == 0 && s.termWidth == 0 oldScrollingHeight := s.termHeight - s.tableHeight s.termWidth, s.termHeight = w, h if s.tableMode { - tableHeight := s.requestedTableHeight - if tableHeight == 0 { - tableHeight = s.termHeight / 4 - if tableHeight < 1 { - tableHeight = 1 - } else if tableHeight > 10 { - tableHeight = 10 - } - } - if tableHeight > s.termHeight-1 { - tableHeight = s.termHeight - 1 - } - s.tableHeight = tableHeight + s.computeTableHeight() scrollingHeight := s.termHeight - s.tableHeight - if !firstUpdate { - // If the scrolling region has changed, attempt to pan the existing text so that it is - // not overwritten by the table. - if scrollingHeight < oldScrollingHeight { - pan := oldScrollingHeight - scrollingHeight - if pan > s.tableHeight { - pan = s.tableHeight - } - fmt.Fprint(s.writer, ansi.panDown(pan)) + // If the scrolling region has changed, attempt to pan the existing text so that it is + // not overwritten by the table. + if scrollingHeight < oldScrollingHeight { + pan := oldScrollingHeight - scrollingHeight + if pan > s.tableHeight { + pan = s.tableHeight } + fmt.Fprint(s.writer, ansi.panDown(pan)) } } } diff --git a/ui/terminal/status.go b/ui/terminal/status.go index d8e739211..2ad174fee 100644 --- a/ui/terminal/status.go +++ b/ui/terminal/status.go @@ -26,12 +26,12 @@ import ( // // statusFormat takes nearly all the same options as NINJA_STATUS. // %c is currently unsupported. -func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild bool) status.StatusOutput { +func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput { formatter := newFormatter(statusFormat, quietBuild) if !forceSimpleOutput && isSmartTerminal(w) { return NewSmartStatusOutput(w, formatter) } else { - return NewSimpleStatusOutput(w, formatter) + return NewSimpleStatusOutput(w, formatter, forceKeepANSI) } } diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go index aa69dff53..810e31d1b 100644 --- a/ui/terminal/status_test.go +++ b/ui/terminal/status_test.go @@ -94,7 +94,7 @@ func TestStatusOutput(t *testing.T) { t.Run("smart", func(t *testing.T) { smart := &fakeSmartTerminal{termWidth: 40} - stat := NewStatusOutput(smart, "", false, false) + stat := NewStatusOutput(smart, "", false, false, false) tt.calls(stat) stat.Flush() @@ -105,7 +105,7 @@ func TestStatusOutput(t *testing.T) { t.Run("simple", func(t *testing.T) { simple := &bytes.Buffer{} - stat := NewStatusOutput(simple, "", false, false) + stat := NewStatusOutput(simple, "", false, false, false) tt.calls(stat) stat.Flush() @@ -116,7 +116,7 @@ func TestStatusOutput(t *testing.T) { t.Run("force simple", func(t *testing.T) { smart := &fakeSmartTerminal{termWidth: 40} - stat := NewStatusOutput(smart, "", true, false) + stat := NewStatusOutput(smart, "", true, false, false) tt.calls(stat) stat.Flush() @@ -269,7 +269,7 @@ func TestSmartStatusOutputWidthChange(t *testing.T) { os.Setenv(tableHeightEnVar, "") smart := &fakeSmartTerminal{termWidth: 40} - stat := NewStatusOutput(smart, "", false, false) + stat := NewStatusOutput(smart, "", false, false, false) smartStat := stat.(*smartStatusOutput) smartStat.sigwinchHandled = make(chan bool)