blob: 2445c5b2cfbfef379a86365d29b32049745d439b [file] [log] [blame]
Dan Willemsenb82471a2018-05-17 16:37:09 -07001// Copyright 2018 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 statusOutput struct {
26 writer Writer
27 format string
28
29 start time.Time
Sasha Smundakc0c9ef92019-01-23 09:52:57 -080030 quiet bool
Dan Willemsenb82471a2018-05-17 16:37:09 -070031}
32
33// NewStatusOutput returns a StatusOutput that represents the
34// current build status similarly to Ninja's built-in terminal
35// output.
36//
37// statusFormat takes nearly all the same options as NINJA_STATUS.
38// %c is currently unsupported.
Sasha Smundakc0c9ef92019-01-23 09:52:57 -080039func NewStatusOutput(w Writer, statusFormat string, quietBuild bool) status.StatusOutput {
Dan Willemsenb82471a2018-05-17 16:37:09 -070040 return &statusOutput{
41 writer: w,
42 format: statusFormat,
43
44 start: time.Now(),
Sasha Smundakc0c9ef92019-01-23 09:52:57 -080045 quiet: quietBuild,
Dan Willemsenb82471a2018-05-17 16:37:09 -070046 }
47}
48
49func (s *statusOutput) Message(level status.MsgLevel, message string) {
Dan Willemsenf78a7342018-07-12 21:26:10 -070050 if level >= status.ErrorLvl {
51 s.writer.Print(fmt.Sprintf("FAILED: %s", message))
52 } else if level > status.StatusLvl {
Dan Willemsenb82471a2018-05-17 16:37:09 -070053 s.writer.Print(fmt.Sprintf("%s%s", level.Prefix(), message))
54 } else if level == status.StatusLvl {
55 s.writer.StatusLine(message)
56 }
57}
58
59func (s *statusOutput) StartAction(action *status.Action, counts status.Counts) {
60 if !s.writer.isSmartTerminal() {
61 return
62 }
63
64 str := action.Description
65 if str == "" {
66 str = action.Command
67 }
68
69 s.writer.StatusLine(s.progress(counts) + str)
70}
71
72func (s *statusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
73 str := result.Description
74 if str == "" {
75 str = result.Command
76 }
77
78 progress := s.progress(counts) + str
79
80 if result.Error != nil {
Sasha Smundakc0c9ef92019-01-23 09:52:57 -080081 targets := strings.Join(result.Outputs, " ")
82 if s.quiet || result.Command == "" {
83 s.writer.StatusAndMessage(progress, fmt.Sprintf("FAILED: %s\n%s", targets, result.Output))
84 } else {
85 s.writer.StatusAndMessage(progress, fmt.Sprintf("FAILED: %s\n%s\n%s", targets, result.Command, result.Output))
Dan Willemsenb82471a2018-05-17 16:37:09 -070086 }
Dan Willemsenb82471a2018-05-17 16:37:09 -070087 } else if result.Output != "" {
88 s.writer.StatusAndMessage(progress, result.Output)
89 } else {
90 s.writer.StatusLine(progress)
91 }
92}
93
94func (s *statusOutput) Flush() {}
95
96func (s *statusOutput) progress(counts status.Counts) string {
97 if s.format == "" {
98 return fmt.Sprintf("[%3d%% %d/%d] ", 100*counts.FinishedActions/counts.TotalActions, counts.FinishedActions, counts.TotalActions)
99 }
100
101 buf := &strings.Builder{}
102 for i := 0; i < len(s.format); i++ {
103 c := s.format[i]
104 if c != '%' {
105 buf.WriteByte(c)
106 continue
107 }
108
109 i = i + 1
110 if i == len(s.format) {
111 buf.WriteByte(c)
112 break
113 }
114
115 c = s.format[i]
116 switch c {
117 case '%':
118 buf.WriteByte(c)
119 case 's':
120 fmt.Fprintf(buf, "%d", counts.StartedActions)
121 case 't':
122 fmt.Fprintf(buf, "%d", counts.TotalActions)
123 case 'r':
124 fmt.Fprintf(buf, "%d", counts.RunningActions)
125 case 'u':
126 fmt.Fprintf(buf, "%d", counts.TotalActions-counts.StartedActions)
127 case 'f':
128 fmt.Fprintf(buf, "%d", counts.FinishedActions)
129 case 'o':
130 fmt.Fprintf(buf, "%.1f", float64(counts.FinishedActions)/time.Since(s.start).Seconds())
131 case 'c':
132 // TODO: implement?
133 buf.WriteRune('?')
134 case 'p':
135 fmt.Fprintf(buf, "%3d%%", 100*counts.FinishedActions/counts.TotalActions)
136 case 'e':
137 fmt.Fprintf(buf, "%.3f", time.Since(s.start).Seconds())
138 default:
139 buf.WriteString("unknown placeholder '")
140 buf.WriteByte(c)
141 buf.WriteString("'")
142 }
143 }
144 return buf.String()
145}