blob: 4205bdc223b9029fcfe9b45981d88c59f2f397fb [file] [log] [blame]
Colin Crossce525352019-06-08 18:58:00 -07001// Copyright 2019 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package terminal
16
17import (
18 "fmt"
19 "strings"
20 "time"
21
22 "android/soong/ui/status"
23)
24
25type formatter struct {
26 format string
27 quiet bool
28 start time.Time
29}
30
31// newFormatter returns a formatter for formatting output to
32// the terminal in a format similar to Ninja.
33// format takes nearly all the same options as NINJA_STATUS.
34// %c is currently unsupported.
35func newFormatter(format string, quiet bool) formatter {
36 return formatter{
37 format: format,
38 quiet: quiet,
39 start: time.Now(),
40 }
41}
42
43func (s formatter) message(level status.MsgLevel, message string) string {
44 if level >= status.ErrorLvl {
45 return fmt.Sprintf("FAILED: %s", message)
46 } else if level > status.StatusLvl {
47 return fmt.Sprintf("%s%s", level.Prefix(), message)
48 } else if level == status.StatusLvl {
49 return message
50 }
51 return ""
52}
53
54func (s formatter) progress(counts status.Counts) string {
55 if s.format == "" {
56 return fmt.Sprintf("[%3d%% %d/%d] ", 100*counts.FinishedActions/counts.TotalActions, counts.FinishedActions, counts.TotalActions)
57 }
58
59 buf := &strings.Builder{}
60 for i := 0; i < len(s.format); i++ {
61 c := s.format[i]
62 if c != '%' {
63 buf.WriteByte(c)
64 continue
65 }
66
67 i = i + 1
68 if i == len(s.format) {
69 buf.WriteByte(c)
70 break
71 }
72
73 c = s.format[i]
74 switch c {
75 case '%':
76 buf.WriteByte(c)
77 case 's':
78 fmt.Fprintf(buf, "%d", counts.StartedActions)
79 case 't':
80 fmt.Fprintf(buf, "%d", counts.TotalActions)
81 case 'r':
82 fmt.Fprintf(buf, "%d", counts.RunningActions)
83 case 'u':
84 fmt.Fprintf(buf, "%d", counts.TotalActions-counts.StartedActions)
85 case 'f':
86 fmt.Fprintf(buf, "%d", counts.FinishedActions)
87 case 'o':
88 fmt.Fprintf(buf, "%.1f", float64(counts.FinishedActions)/time.Since(s.start).Seconds())
89 case 'c':
90 // TODO: implement?
91 buf.WriteRune('?')
92 case 'p':
93 fmt.Fprintf(buf, "%3d%%", 100*counts.FinishedActions/counts.TotalActions)
94 case 'e':
95 fmt.Fprintf(buf, "%.3f", time.Since(s.start).Seconds())
96 default:
97 buf.WriteString("unknown placeholder '")
98 buf.WriteByte(c)
99 buf.WriteString("'")
100 }
101 }
102 return buf.String()
103}
104
105func (s formatter) result(result status.ActionResult) string {
106 var ret string
107 if result.Error != nil {
108 targets := strings.Join(result.Outputs, " ")
109 if s.quiet || result.Command == "" {
110 ret = fmt.Sprintf("FAILED: %s\n%s", targets, result.Output)
111 } else {
112 ret = fmt.Sprintf("FAILED: %s\n%s\n%s", targets, result.Command, result.Output)
113 }
114 } else if result.Output != "" {
115 ret = result.Output
116 }
117
118 if len(ret) > 0 && ret[len(ret)-1] != '\n' {
119 ret += "\n"
120 }
121
122 return ret
123}