blob: 14df346de2fec1d6182709dd7f3f35fc2e5718cc [file] [log] [blame]
Dan Willemsenb82471a2018-05-17 16:37:09 -07001// 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 (
Dan Willemsenb82471a2018-05-17 16:37:09 -070018 "compress/gzip"
Patrice Arruda297ceba2019-06-06 16:44:37 -070019 "errors"
Dan Willemsenb82471a2018-05-17 16:37:09 -070020 "fmt"
21 "io"
Patrice Arruda297ceba2019-06-06 16:44:37 -070022 "io/ioutil"
Patrice Arruda36b5e312020-03-11 12:41:47 -070023 "os"
Dan Willemsenb82471a2018-05-17 16:37:09 -070024 "strings"
Patrice Arruda297ceba2019-06-06 16:44:37 -070025
Dan Willemsen4591b642021-05-24 14:24:12 -070026 "google.golang.org/protobuf/proto"
Patrice Arruda297ceba2019-06-06 16:44:37 -070027
28 "android/soong/ui/logger"
Dan Willemsen4591b642021-05-24 14:24:12 -070029 soong_build_error_proto "android/soong/ui/status/build_error_proto"
30 soong_build_progress_proto "android/soong/ui/status/build_progress_proto"
Dan Willemsenb82471a2018-05-17 16:37:09 -070031)
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
Colin Crosse0df1a32019-06-09 19:40:08 -070082func (v *verboseLog) Write(p []byte) (int, error) {
83 fmt.Fprint(v.w, string(p))
84 return len(p), nil
85}
86
Dan Willemsenb82471a2018-05-17 16:37:09 -070087type errorLog struct {
Patrice Arruda297ceba2019-06-06 16:44:37 -070088 w io.WriteCloser
Dan Willemsenb82471a2018-05-17 16:37:09 -070089 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
Dan Willemsenb82471a2018-05-17 16:37:09 -0700112 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)
Patrice Arruda297ceba2019-06-06 16:44:37 -0700118
Dan Willemsenb82471a2018-05-17 16:37:09 -0700119 if len(result.Outputs) > 0 {
120 fmt.Fprintf(e.w, "Outputs: %s\n", strings.Join(result.Outputs, " "))
121 }
Patrice Arruda297ceba2019-06-06 16:44:37 -0700122
Dan Willemsenb82471a2018-05-17 16:37:09 -0700123 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}
Colin Crosse0df1a32019-06-09 19:40:08 -0700146
147func (e *errorLog) Write(p []byte) (int, error) {
148 fmt.Fprint(e.w, string(p))
149 return len(p), nil
150}
Patrice Arruda297ceba2019-06-06 16:44:37 -0700151
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 {
Patrice Arruda36b5e312020-03-11 12:41:47 -0700159 os.Remove(filename)
Patrice Arruda297ceba2019-06-06 16:44:37 -0700160 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 })
Patrice Arruda297ceba2019-06-06 16:44:37 -0700181
Patrice Arruda85f227e2020-03-19 18:23:26 -0700182 err := writeToFile(&e.errorProto, e.filename)
Patrice Arruda297ceba2019-06-06 16:44:37 -0700183 if err != nil {
Colin Crossff27ce42019-11-26 16:20:03 -0800184 e.log.Printf("Failed to write file %s: %v\n", e.filename, err)
Patrice Arruda297ceba2019-06-06 16:44:37 -0700185 }
186}
187
Patrice Arruda85f227e2020-03-19 18:23:26 -0700188func (e *errorProtoLog) Flush() {
189 //Not required.
190}
191
Patrice Arruda297ceba2019-06-06 16:44:37 -0700192func (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}
Patrice Arruda74b43992020-03-11 08:21:05 -0700201
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}