• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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
30	quiet bool
31}
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.
39func NewStatusOutput(w Writer, statusFormat string, quietBuild bool) status.StatusOutput {
40	return &statusOutput{
41		writer: w,
42		format: statusFormat,
43
44		start: time.Now(),
45		quiet: quietBuild,
46	}
47}
48
49func (s *statusOutput) Message(level status.MsgLevel, message string) {
50	if level >= status.ErrorLvl {
51		s.writer.Print(fmt.Sprintf("FAILED: %s", message))
52	} else if level > status.StatusLvl {
53		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 {
81		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))
86		}
87	} 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}
146