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