| // Copyright 2018 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package status |
| |
| import ( |
| "compress/gzip" |
| "errors" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "os" |
| "strings" |
| |
| "google.golang.org/protobuf/proto" |
| |
| "android/soong/ui/logger" |
| soong_build_error_proto "android/soong/ui/status/build_error_proto" |
| soong_build_progress_proto "android/soong/ui/status/build_progress_proto" |
| ) |
| |
| type verboseLog struct { |
| w io.WriteCloser |
| } |
| |
| func NewVerboseLog(log logger.Logger, filename string) StatusOutput { |
| if !strings.HasSuffix(filename, ".gz") { |
| filename += ".gz" |
| } |
| |
| f, err := logger.CreateFileWithRotation(filename, 5) |
| if err != nil { |
| log.Println("Failed to create verbose log file:", err) |
| return nil |
| } |
| |
| w := gzip.NewWriter(f) |
| |
| return &verboseLog{ |
| w: w, |
| } |
| } |
| |
| func (v *verboseLog) StartAction(action *Action, counts Counts) {} |
| |
| func (v *verboseLog) FinishAction(result ActionResult, counts Counts) { |
| cmd := result.Command |
| if cmd == "" { |
| cmd = result.Description |
| } |
| |
| fmt.Fprintf(v.w, "[%d/%d] %s\n", counts.FinishedActions, counts.TotalActions, cmd) |
| |
| if result.Error != nil { |
| fmt.Fprintf(v.w, "FAILED: %s\n", strings.Join(result.Outputs, " ")) |
| } |
| |
| if result.Output != "" { |
| fmt.Fprintln(v.w, result.Output) |
| } |
| } |
| |
| func (v *verboseLog) Flush() { |
| v.w.Close() |
| } |
| |
| func (v *verboseLog) Message(level MsgLevel, message string) { |
| fmt.Fprintf(v.w, "%s%s\n", level.Prefix(), message) |
| } |
| |
| func (v *verboseLog) Write(p []byte) (int, error) { |
| fmt.Fprint(v.w, string(p)) |
| return len(p), nil |
| } |
| |
| type errorLog struct { |
| w io.WriteCloser |
| empty bool |
| } |
| |
| func NewErrorLog(log logger.Logger, filename string) StatusOutput { |
| f, err := logger.CreateFileWithRotation(filename, 5) |
| if err != nil { |
| log.Println("Failed to create error log file:", err) |
| return nil |
| } |
| |
| return &errorLog{ |
| w: f, |
| empty: true, |
| } |
| } |
| |
| func (e *errorLog) StartAction(action *Action, counts Counts) {} |
| |
| func (e *errorLog) FinishAction(result ActionResult, counts Counts) { |
| if result.Error == nil { |
| return |
| } |
| |
| if !e.empty { |
| fmt.Fprintf(e.w, "\n\n") |
| } |
| e.empty = false |
| |
| fmt.Fprintf(e.w, "FAILED: %s\n", result.Description) |
| |
| if len(result.Outputs) > 0 { |
| fmt.Fprintf(e.w, "Outputs: %s\n", strings.Join(result.Outputs, " ")) |
| } |
| |
| fmt.Fprintf(e.w, "Error: %s\n", result.Error) |
| if result.Command != "" { |
| fmt.Fprintf(e.w, "Command: %s\n", result.Command) |
| } |
| fmt.Fprintf(e.w, "Output:\n%s\n", result.Output) |
| } |
| |
| func (e *errorLog) Flush() { |
| e.w.Close() |
| } |
| |
| func (e *errorLog) Message(level MsgLevel, message string) { |
| if level < ErrorLvl { |
| return |
| } |
| |
| if !e.empty { |
| fmt.Fprintf(e.w, "\n\n") |
| } |
| e.empty = false |
| |
| fmt.Fprintf(e.w, "error: %s\n", message) |
| } |
| |
| func (e *errorLog) Write(p []byte) (int, error) { |
| fmt.Fprint(e.w, string(p)) |
| return len(p), nil |
| } |
| |
| type errorProtoLog struct { |
| errorProto soong_build_error_proto.BuildError |
| filename string |
| log logger.Logger |
| } |
| |
| func NewProtoErrorLog(log logger.Logger, filename string) StatusOutput { |
| os.Remove(filename) |
| return &errorProtoLog{ |
| errorProto: soong_build_error_proto.BuildError{}, |
| filename: filename, |
| log: log, |
| } |
| } |
| |
| func (e *errorProtoLog) StartAction(action *Action, counts Counts) {} |
| |
| func (e *errorProtoLog) FinishAction(result ActionResult, counts Counts) { |
| if result.Error == nil { |
| return |
| } |
| |
| e.errorProto.ActionErrors = append(e.errorProto.ActionErrors, &soong_build_error_proto.BuildActionError{ |
| Description: proto.String(result.Description), |
| Command: proto.String(result.Command), |
| Output: proto.String(result.Output), |
| Artifacts: result.Outputs, |
| Error: proto.String(result.Error.Error()), |
| }) |
| |
| err := writeToFile(&e.errorProto, e.filename) |
| if err != nil { |
| e.log.Printf("Failed to write file %s: %v\n", e.filename, err) |
| } |
| } |
| |
| func (e *errorProtoLog) Flush() { |
| //Not required. |
| } |
| |
| func (e *errorProtoLog) Message(level MsgLevel, message string) { |
| if level > ErrorLvl { |
| e.errorProto.ErrorMessages = append(e.errorProto.ErrorMessages, message) |
| } |
| } |
| |
| func (e *errorProtoLog) Write(p []byte) (int, error) { |
| return 0, errors.New("not supported") |
| } |
| |
| type buildProgressLog struct { |
| filename string |
| log logger.Logger |
| failedActions uint64 |
| } |
| |
| func NewBuildProgressLog(log logger.Logger, filename string) StatusOutput { |
| return &buildProgressLog{ |
| filename: filename, |
| log: log, |
| failedActions: 0, |
| } |
| } |
| |
| func (b *buildProgressLog) StartAction(action *Action, counts Counts) { |
| b.updateCounters(counts) |
| } |
| |
| func (b *buildProgressLog) FinishAction(result ActionResult, counts Counts) { |
| if result.Error != nil { |
| b.failedActions++ |
| } |
| b.updateCounters(counts) |
| } |
| |
| func (b *buildProgressLog) Flush() { |
| //Not required. |
| } |
| |
| func (b *buildProgressLog) Message(level MsgLevel, message string) { |
| // Not required. |
| } |
| |
| func (b *buildProgressLog) Write(p []byte) (int, error) { |
| return 0, errors.New("not supported") |
| } |
| |
| func (b *buildProgressLog) updateCounters(counts Counts) { |
| err := writeToFile( |
| &soong_build_progress_proto.BuildProgress{ |
| CurrentActions: proto.Uint64(uint64(counts.RunningActions)), |
| FinishedActions: proto.Uint64(uint64(counts.FinishedActions)), |
| TotalActions: proto.Uint64(uint64(counts.TotalActions)), |
| FailedActions: proto.Uint64(b.failedActions), |
| }, |
| b.filename, |
| ) |
| if err != nil { |
| b.log.Printf("Failed to write file %s: %v\n", b.filename, err) |
| } |
| } |
| |
| func writeToFile(pb proto.Message, outputPath string) (err error) { |
| data, err := proto.Marshal(pb) |
| if err != nil { |
| return err |
| } |
| |
| tempPath := outputPath + ".tmp" |
| err = ioutil.WriteFile(tempPath, []byte(data), 0644) |
| if err != nil { |
| return err |
| } |
| |
| err = os.Rename(tempPath, outputPath) |
| if err != nil { |
| return err |
| } |
| |
| return nil |
| } |