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 "strings" 24 25 "github.com/golang/protobuf/proto" 26 27 "android/soong/ui/logger" 28 "android/soong/ui/status/build_error_proto" 29) 30 31type verboseLog struct { 32 w io.WriteCloser 33} 34 35func NewVerboseLog(log logger.Logger, filename string) StatusOutput { 36 if !strings.HasSuffix(filename, ".gz") { 37 filename += ".gz" 38 } 39 40 f, err := logger.CreateFileWithRotation(filename, 5) 41 if err != nil { 42 log.Println("Failed to create verbose log file:", err) 43 return nil 44 } 45 46 w := gzip.NewWriter(f) 47 48 return &verboseLog{ 49 w: w, 50 } 51} 52 53func (v *verboseLog) StartAction(action *Action, counts Counts) {} 54 55func (v *verboseLog) FinishAction(result ActionResult, counts Counts) { 56 cmd := result.Command 57 if cmd == "" { 58 cmd = result.Description 59 } 60 61 fmt.Fprintf(v.w, "[%d/%d] %s\n", counts.FinishedActions, counts.TotalActions, cmd) 62 63 if result.Error != nil { 64 fmt.Fprintf(v.w, "FAILED: %s\n", strings.Join(result.Outputs, " ")) 65 } 66 67 if result.Output != "" { 68 fmt.Fprintln(v.w, result.Output) 69 } 70} 71 72func (v *verboseLog) Flush() { 73 v.w.Close() 74} 75 76func (v *verboseLog) Message(level MsgLevel, message string) { 77 fmt.Fprintf(v.w, "%s%s\n", level.Prefix(), message) 78} 79 80func (v *verboseLog) Write(p []byte) (int, error) { 81 fmt.Fprint(v.w, string(p)) 82 return len(p), nil 83} 84 85type errorLog struct { 86 w io.WriteCloser 87 empty bool 88} 89 90func NewErrorLog(log logger.Logger, filename string) StatusOutput { 91 f, err := logger.CreateFileWithRotation(filename, 5) 92 if err != nil { 93 log.Println("Failed to create error log file:", err) 94 return nil 95 } 96 97 return &errorLog{ 98 w: f, 99 empty: true, 100 } 101} 102 103func (e *errorLog) StartAction(action *Action, counts Counts) {} 104 105func (e *errorLog) FinishAction(result ActionResult, counts Counts) { 106 if result.Error == nil { 107 return 108 } 109 110 if !e.empty { 111 fmt.Fprintf(e.w, "\n\n") 112 } 113 e.empty = false 114 115 fmt.Fprintf(e.w, "FAILED: %s\n", result.Description) 116 117 if len(result.Outputs) > 0 { 118 fmt.Fprintf(e.w, "Outputs: %s\n", strings.Join(result.Outputs, " ")) 119 } 120 121 fmt.Fprintf(e.w, "Error: %s\n", result.Error) 122 if result.Command != "" { 123 fmt.Fprintf(e.w, "Command: %s\n", result.Command) 124 } 125 fmt.Fprintf(e.w, "Output:\n%s\n", result.Output) 126} 127 128func (e *errorLog) Flush() { 129 e.w.Close() 130} 131 132func (e *errorLog) Message(level MsgLevel, message string) { 133 if level < ErrorLvl { 134 return 135 } 136 137 if !e.empty { 138 fmt.Fprintf(e.w, "\n\n") 139 } 140 e.empty = false 141 142 fmt.Fprintf(e.w, "error: %s\n", message) 143} 144 145func (e *errorLog) Write(p []byte) (int, error) { 146 fmt.Fprint(e.w, string(p)) 147 return len(p), nil 148} 149 150type errorProtoLog struct { 151 errorProto soong_build_error_proto.BuildError 152 filename string 153 log logger.Logger 154} 155 156func NewProtoErrorLog(log logger.Logger, filename string) StatusOutput { 157 return &errorProtoLog{ 158 errorProto: soong_build_error_proto.BuildError{}, 159 filename: filename, 160 log: log, 161 } 162} 163 164func (e *errorProtoLog) StartAction(action *Action, counts Counts) {} 165 166func (e *errorProtoLog) FinishAction(result ActionResult, counts Counts) { 167 if result.Error == nil { 168 return 169 } 170 171 e.errorProto.ActionErrors = append(e.errorProto.ActionErrors, &soong_build_error_proto.BuildActionError{ 172 Description: proto.String(result.Description), 173 Command: proto.String(result.Command), 174 Output: proto.String(result.Output), 175 Artifacts: result.Outputs, 176 Error: proto.String(result.Error.Error()), 177 }) 178} 179 180func (e *errorProtoLog) Flush() { 181 data, err := proto.Marshal(&e.errorProto) 182 if err != nil { 183 e.log.Printf("Failed to marshal build status proto: %v\n", err) 184 return 185 } 186 err = ioutil.WriteFile(e.filename, []byte(data), 0644) 187 if err != nil { 188 e.log.Printf("Failed to write file %s: %v\n", e.filename, err) 189 } 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