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 status 16 17import ( 18 "compress/gzip" 19 "errors" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "os" 24 "strings" 25 26 "github.com/golang/protobuf/proto" 27 28 "android/soong/ui/logger" 29 "android/soong/ui/status/build_error_proto" 30 "android/soong/ui/status/build_progress_proto" 31) 32 33type verboseLog struct { 34 w io.WriteCloser 35} 36 37func NewVerboseLog(log logger.Logger, filename string) StatusOutput { 38 if !strings.HasSuffix(filename, ".gz") { 39 filename += ".gz" 40 } 41 42 f, err := logger.CreateFileWithRotation(filename, 5) 43 if err != nil { 44 log.Println("Failed to create verbose log file:", err) 45 return nil 46 } 47 48 w := gzip.NewWriter(f) 49 50 return &verboseLog{ 51 w: w, 52 } 53} 54 55func (v *verboseLog) StartAction(action *Action, counts Counts) {} 56 57func (v *verboseLog) FinishAction(result ActionResult, counts Counts) { 58 cmd := result.Command 59 if cmd == "" { 60 cmd = result.Description 61 } 62 63 fmt.Fprintf(v.w, "[%d/%d] %s\n", counts.FinishedActions, counts.TotalActions, cmd) 64 65 if result.Error != nil { 66 fmt.Fprintf(v.w, "FAILED: %s\n", strings.Join(result.Outputs, " ")) 67 } 68 69 if result.Output != "" { 70 fmt.Fprintln(v.w, result.Output) 71 } 72} 73 74func (v *verboseLog) Flush() { 75 v.w.Close() 76} 77 78func (v *verboseLog) Message(level MsgLevel, message string) { 79 fmt.Fprintf(v.w, "%s%s\n", level.Prefix(), message) 80} 81 82func (v *verboseLog) Write(p []byte) (int, error) { 83 fmt.Fprint(v.w, string(p)) 84 return len(p), nil 85} 86 87type errorLog struct { 88 w io.WriteCloser 89 empty bool 90} 91 92func NewErrorLog(log logger.Logger, filename string) StatusOutput { 93 f, err := logger.CreateFileWithRotation(filename, 5) 94 if err != nil { 95 log.Println("Failed to create error log file:", err) 96 return nil 97 } 98 99 return &errorLog{ 100 w: f, 101 empty: true, 102 } 103} 104 105func (e *errorLog) StartAction(action *Action, counts Counts) {} 106 107func (e *errorLog) FinishAction(result ActionResult, counts Counts) { 108 if result.Error == nil { 109 return 110 } 111 112 if !e.empty { 113 fmt.Fprintf(e.w, "\n\n") 114 } 115 e.empty = false 116 117 fmt.Fprintf(e.w, "FAILED: %s\n", result.Description) 118 119 if len(result.Outputs) > 0 { 120 fmt.Fprintf(e.w, "Outputs: %s\n", strings.Join(result.Outputs, " ")) 121 } 122 123 fmt.Fprintf(e.w, "Error: %s\n", result.Error) 124 if result.Command != "" { 125 fmt.Fprintf(e.w, "Command: %s\n", result.Command) 126 } 127 fmt.Fprintf(e.w, "Output:\n%s\n", result.Output) 128} 129 130func (e *errorLog) Flush() { 131 e.w.Close() 132} 133 134func (e *errorLog) Message(level MsgLevel, message string) { 135 if level < ErrorLvl { 136 return 137 } 138 139 if !e.empty { 140 fmt.Fprintf(e.w, "\n\n") 141 } 142 e.empty = false 143 144 fmt.Fprintf(e.w, "error: %s\n", message) 145} 146 147func (e *errorLog) Write(p []byte) (int, error) { 148 fmt.Fprint(e.w, string(p)) 149 return len(p), nil 150} 151 152type errorProtoLog struct { 153 errorProto soong_build_error_proto.BuildError 154 filename string 155 log logger.Logger 156} 157 158func NewProtoErrorLog(log logger.Logger, filename string) StatusOutput { 159 os.Remove(filename) 160 return &errorProtoLog{ 161 errorProto: soong_build_error_proto.BuildError{}, 162 filename: filename, 163 log: log, 164 } 165} 166 167func (e *errorProtoLog) StartAction(action *Action, counts Counts) {} 168 169func (e *errorProtoLog) FinishAction(result ActionResult, counts Counts) { 170 if result.Error == nil { 171 return 172 } 173 174 e.errorProto.ActionErrors = append(e.errorProto.ActionErrors, &soong_build_error_proto.BuildActionError{ 175 Description: proto.String(result.Description), 176 Command: proto.String(result.Command), 177 Output: proto.String(result.Output), 178 Artifacts: result.Outputs, 179 Error: proto.String(result.Error.Error()), 180 }) 181 182 err := writeToFile(&e.errorProto, e.filename) 183 if err != nil { 184 e.log.Printf("Failed to write file %s: %v\n", e.filename, err) 185 } 186} 187 188func (e *errorProtoLog) Flush() { 189 //Not required. 190} 191 192func (e *errorProtoLog) Message(level MsgLevel, message string) { 193 if level > ErrorLvl { 194 e.errorProto.ErrorMessages = append(e.errorProto.ErrorMessages, message) 195 } 196} 197 198func (e *errorProtoLog) Write(p []byte) (int, error) { 199 return 0, errors.New("not supported") 200} 201 202type buildProgressLog struct { 203 filename string 204 log logger.Logger 205 failedActions uint64 206} 207 208func NewBuildProgressLog(log logger.Logger, filename string) StatusOutput { 209 return &buildProgressLog{ 210 filename: filename, 211 log: log, 212 failedActions: 0, 213 } 214} 215 216func (b *buildProgressLog) StartAction(action *Action, counts Counts) { 217 b.updateCounters(counts) 218} 219 220func (b *buildProgressLog) FinishAction(result ActionResult, counts Counts) { 221 if result.Error != nil { 222 b.failedActions++ 223 } 224 b.updateCounters(counts) 225} 226 227func (b *buildProgressLog) Flush() { 228 //Not required. 229} 230 231func (b *buildProgressLog) Message(level MsgLevel, message string) { 232 // Not required. 233} 234 235func (b *buildProgressLog) Write(p []byte) (int, error) { 236 return 0, errors.New("not supported") 237} 238 239func (b *buildProgressLog) updateCounters(counts Counts) { 240 err := writeToFile( 241 &soong_build_progress_proto.BuildProgress{ 242 CurrentActions: proto.Uint64(uint64(counts.RunningActions)), 243 FinishedActions: proto.Uint64(uint64(counts.FinishedActions)), 244 TotalActions: proto.Uint64(uint64(counts.TotalActions)), 245 FailedActions: proto.Uint64(b.failedActions), 246 }, 247 b.filename, 248 ) 249 if err != nil { 250 b.log.Printf("Failed to write file %s: %v\n", b.filename, err) 251 } 252} 253 254func writeToFile(pb proto.Message, outputPath string) (err error) { 255 data, err := proto.Marshal(pb) 256 if err != nil { 257 return err 258 } 259 260 tempPath := outputPath + ".tmp" 261 err = ioutil.WriteFile(tempPath, []byte(data), 0644) 262 if err != nil { 263 return err 264 } 265 266 err = os.Rename(tempPath, outputPath) 267 if err != nil { 268 return err 269 } 270 271 return nil 272} 273