// Copyright 2019 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 formatter struct { format string quiet bool start time.Time } // newFormatter returns a formatter for formatting output to // the terminal in a format similar to Ninja. // format takes nearly all the same options as NINJA_STATUS. // %c is currently unsupported. func newFormatter(format string, quiet bool) formatter { return formatter{ format: format, quiet: quiet, start: time.Now(), } } func (s formatter) message(level status.MsgLevel, message string) string { if level >= status.ErrorLvl { return fmt.Sprintf("FAILED: %s", message) } else if level > status.StatusLvl { return fmt.Sprintf("%s%s", level.Prefix(), message) } else if level == status.StatusLvl { return message } return "" } func (s formatter) 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() } func (s formatter) result(result status.ActionResult) string { var ret string if result.Error != nil { targets := strings.Join(result.Outputs, " ") if s.quiet || result.Command == "" { ret = fmt.Sprintf("FAILED: %s\n%s", targets, result.Output) } else { ret = fmt.Sprintf("FAILED: %s\n%s\n%s", targets, result.Command, result.Output) } } else if result.Output != "" { ret = result.Output } if len(ret) > 0 && ret[len(ret)-1] != '\n' { ret += "\n" } return ret }