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 15 package status 16 17 import ( 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 33 type verboseLog struct { 34 w io.WriteCloser 35 } 36 37 func 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 55 func (v *verboseLog) StartAction(action *Action, counts Counts) {} 56 57 func (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 74 func (v *verboseLog) Flush() { 75 v.w.Close() 76 } 77 78 func (v *verboseLog) Message(level MsgLevel, message string) { 79 fmt.Fprintf(v.w, "%s%s\n", level.Prefix(), message) 80 } 81 82 func (v *verboseLog) Write(p []byte) (int, error) { 83 fmt.Fprint(v.w, string(p)) 84 return len(p), nil 85 } 86 87 type errorLog struct { 88 w io.WriteCloser 89 empty bool 90 } 91 92 func 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 105 func (e *errorLog) StartAction(action *Action, counts Counts) {} 106 107 func (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 130 func (e *errorLog) Flush() { 131 e.w.Close() 132 } 133 134 func (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 147 func (e *errorLog) Write(p []byte) (int, error) { 148 fmt.Fprint(e.w, string(p)) 149 return len(p), nil 150 } 151 152 type errorProtoLog struct { 153 errorProto soong_build_error_proto.BuildError 154 filename string 155 log logger.Logger 156 } 157 158 func 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 167 func (e *errorProtoLog) StartAction(action *Action, counts Counts) {} 168 169 func (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 188 func (e *errorProtoLog) Flush() { 189 //Not required. 190 } 191 192 func (e *errorProtoLog) Message(level MsgLevel, message string) { 193 if level > ErrorLvl { 194 e.errorProto.ErrorMessages = append(e.errorProto.ErrorMessages, message) 195 } 196 } 197 198 func (e *errorProtoLog) Write(p []byte) (int, error) { 199 return 0, errors.New("not supported") 200 } 201 202 type buildProgressLog struct { 203 filename string 204 log logger.Logger 205 failedActions uint64 206 } 207 208 func NewBuildProgressLog(log logger.Logger, filename string) StatusOutput { 209 return &buildProgressLog{ 210 filename: filename, 211 log: log, 212 failedActions: 0, 213 } 214 } 215 216 func (b *buildProgressLog) StartAction(action *Action, counts Counts) { 217 b.updateCounters(counts) 218 } 219 220 func (b *buildProgressLog) FinishAction(result ActionResult, counts Counts) { 221 if result.Error != nil { 222 b.failedActions++ 223 } 224 b.updateCounters(counts) 225 } 226 227 func (b *buildProgressLog) Flush() { 228 //Not required. 229 } 230 231 func (b *buildProgressLog) Message(level MsgLevel, message string) { 232 // Not required. 233 } 234 235 func (b *buildProgressLog) Write(p []byte) (int, error) { 236 return 0, errors.New("not supported") 237 } 238 239 func (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 254 func 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