f78a73444e
I've noticed a few instances of interleaved status messages in between lines in a terminal/Writer.Print call on our build servers. Since there's a lock protecting everything we write, I've got to assume this is a stdout vs stderr problem. Ninja had always been outputing to stdout, except for error messages, which are now marked with FAILED: like failed actions. Test: m blueprint_tools Test: m missing Change-Id: Idf8320d40694abf212c902c63a9703e4440ffb7a
144 lines
3.6 KiB
Go
144 lines
3.6 KiB
Go
// Copyright 2018 Google Inc. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package terminal
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"android/soong/ui/status"
|
|
)
|
|
|
|
type statusOutput struct {
|
|
writer Writer
|
|
format string
|
|
|
|
start time.Time
|
|
}
|
|
|
|
// NewStatusOutput returns a StatusOutput that represents the
|
|
// current build status similarly to Ninja's built-in terminal
|
|
// output.
|
|
//
|
|
// statusFormat takes nearly all the same options as NINJA_STATUS.
|
|
// %c is currently unsupported.
|
|
func NewStatusOutput(w Writer, statusFormat string) status.StatusOutput {
|
|
return &statusOutput{
|
|
writer: w,
|
|
format: statusFormat,
|
|
|
|
start: time.Now(),
|
|
}
|
|
}
|
|
|
|
func (s *statusOutput) Message(level status.MsgLevel, message string) {
|
|
if level >= status.ErrorLvl {
|
|
s.writer.Print(fmt.Sprintf("FAILED: %s", message))
|
|
} else if level > status.StatusLvl {
|
|
s.writer.Print(fmt.Sprintf("%s%s", level.Prefix(), message))
|
|
} else if level == status.StatusLvl {
|
|
s.writer.StatusLine(message)
|
|
}
|
|
}
|
|
|
|
func (s *statusOutput) StartAction(action *status.Action, counts status.Counts) {
|
|
if !s.writer.isSmartTerminal() {
|
|
return
|
|
}
|
|
|
|
str := action.Description
|
|
if str == "" {
|
|
str = action.Command
|
|
}
|
|
|
|
s.writer.StatusLine(s.progress(counts) + str)
|
|
}
|
|
|
|
func (s *statusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
|
|
str := result.Description
|
|
if str == "" {
|
|
str = result.Command
|
|
}
|
|
|
|
progress := s.progress(counts) + str
|
|
|
|
if result.Error != nil {
|
|
hasCommand := ""
|
|
if result.Command != "" {
|
|
hasCommand = "\n"
|
|
}
|
|
|
|
s.writer.StatusAndMessage(progress, fmt.Sprintf("FAILED: %s\n%s%s%s",
|
|
strings.Join(result.Outputs, " "), result.Command, hasCommand, result.Output))
|
|
} else if result.Output != "" {
|
|
s.writer.StatusAndMessage(progress, result.Output)
|
|
} else {
|
|
s.writer.StatusLine(progress)
|
|
}
|
|
}
|
|
|
|
func (s *statusOutput) Flush() {}
|
|
|
|
func (s *statusOutput) progress(counts status.Counts) string {
|
|
if s.format == "" {
|
|
return fmt.Sprintf("[%3d%% %d/%d] ", 100*counts.FinishedActions/counts.TotalActions, counts.FinishedActions, counts.TotalActions)
|
|
}
|
|
|
|
buf := &strings.Builder{}
|
|
for i := 0; i < len(s.format); i++ {
|
|
c := s.format[i]
|
|
if c != '%' {
|
|
buf.WriteByte(c)
|
|
continue
|
|
}
|
|
|
|
i = i + 1
|
|
if i == len(s.format) {
|
|
buf.WriteByte(c)
|
|
break
|
|
}
|
|
|
|
c = s.format[i]
|
|
switch c {
|
|
case '%':
|
|
buf.WriteByte(c)
|
|
case 's':
|
|
fmt.Fprintf(buf, "%d", counts.StartedActions)
|
|
case 't':
|
|
fmt.Fprintf(buf, "%d", counts.TotalActions)
|
|
case 'r':
|
|
fmt.Fprintf(buf, "%d", counts.RunningActions)
|
|
case 'u':
|
|
fmt.Fprintf(buf, "%d", counts.TotalActions-counts.StartedActions)
|
|
case 'f':
|
|
fmt.Fprintf(buf, "%d", counts.FinishedActions)
|
|
case 'o':
|
|
fmt.Fprintf(buf, "%.1f", float64(counts.FinishedActions)/time.Since(s.start).Seconds())
|
|
case 'c':
|
|
// TODO: implement?
|
|
buf.WriteRune('?')
|
|
case 'p':
|
|
fmt.Fprintf(buf, "%3d%%", 100*counts.FinishedActions/counts.TotalActions)
|
|
case 'e':
|
|
fmt.Fprintf(buf, "%.3f", time.Since(s.start).Seconds())
|
|
default:
|
|
buf.WriteString("unknown placeholder '")
|
|
buf.WriteByte(c)
|
|
buf.WriteString("'")
|
|
}
|
|
}
|
|
return buf.String()
|
|
}
|