blob: 7bfd39641360cbf309a178f3181c695f8926af9d [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"
Colin Crosscdeb7bb2024-11-19 22:08:19 -080025 "sync"
26 "time"
Patrice Arruda297ceba2019-06-06 16:44:37 -070027
Dan Willemsen4591b642021-05-24 14:24:12 -070028 "google.golang.org/protobuf/proto"
Patrice Arruda297ceba2019-06-06 16:44:37 -070029
30 "android/soong/ui/logger"
Dan Willemsen4591b642021-05-24 14:24:12 -070031 soong_build_error_proto "android/soong/ui/status/build_error_proto"
32 soong_build_progress_proto "android/soong/ui/status/build_progress_proto"
Dan Willemsenb82471a2018-05-17 16:37:09 -070033)
34
35type verboseLog struct {
Colin Crosscdeb7bb2024-11-19 22:08:19 -080036 w *gzip.Writer
37 lock *sync.Mutex
38 data chan []string
39 stop chan bool
Dan Willemsenb82471a2018-05-17 16:37:09 -070040}
41
42func NewVerboseLog(log logger.Logger, filename string) StatusOutput {
43 if !strings.HasSuffix(filename, ".gz") {
44 filename += ".gz"
45 }
46
47 f, err := logger.CreateFileWithRotation(filename, 5)
48 if err != nil {
49 log.Println("Failed to create verbose log file:", err)
50 return nil
51 }
52
53 w := gzip.NewWriter(f)
54
Colin Crosscdeb7bb2024-11-19 22:08:19 -080055 l := &verboseLog{
56 w: w,
57 lock: &sync.Mutex{},
58 data: make(chan []string),
59 stop: make(chan bool),
Dan Willemsenb82471a2018-05-17 16:37:09 -070060 }
Colin Crosscdeb7bb2024-11-19 22:08:19 -080061 l.startWriter()
62 return l
63}
64
65func (v *verboseLog) startWriter() {
66 go func() {
67 tick := time.Tick(time.Second)
68 for {
69 select {
70 case <-v.stop:
71 close(v.data)
72 v.w.Close()
73 return
74 case <-tick:
75 v.w.Flush()
76 case dataList := <-v.data:
77 for _, data := range dataList {
78 fmt.Fprint(v.w, data)
79 }
80 }
81 }
82 }()
83}
84
85func (v *verboseLog) stopWriter() {
86 v.stop <- true
87}
88
89func (v *verboseLog) queueWrite(s ...string) {
90 v.data <- s
Dan Willemsenb82471a2018-05-17 16:37:09 -070091}
92
93func (v *verboseLog) StartAction(action *Action, counts Counts) {}
94
95func (v *verboseLog) FinishAction(result ActionResult, counts Counts) {
96 cmd := result.Command
97 if cmd == "" {
98 cmd = result.Description
99 }
100
Colin Crosscdeb7bb2024-11-19 22:08:19 -0800101 v.queueWrite(fmt.Sprintf("[%d/%d] ", counts.FinishedActions, counts.TotalActions), cmd, "\n")
Dan Willemsenb82471a2018-05-17 16:37:09 -0700102
103 if result.Error != nil {
Colin Crosscdeb7bb2024-11-19 22:08:19 -0800104 v.queueWrite("FAILED: ", strings.Join(result.Outputs, " "), "\n")
Dan Willemsenb82471a2018-05-17 16:37:09 -0700105 }
106
107 if result.Output != "" {
Colin Crosscdeb7bb2024-11-19 22:08:19 -0800108 v.queueWrite(result.Output, "\n")
Dan Willemsenb82471a2018-05-17 16:37:09 -0700109 }
110}
111
112func (v *verboseLog) Flush() {
Colin Crosscdeb7bb2024-11-19 22:08:19 -0800113 v.stopWriter()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700114}
115
116func (v *verboseLog) Message(level MsgLevel, message string) {
Colin Crosscdeb7bb2024-11-19 22:08:19 -0800117 v.queueWrite(level.Prefix(), message, "\n")
Dan Willemsenb82471a2018-05-17 16:37:09 -0700118}
119
Colin Crosse0df1a32019-06-09 19:40:08 -0700120func (v *verboseLog) Write(p []byte) (int, error) {
Colin Crosscdeb7bb2024-11-19 22:08:19 -0800121 v.queueWrite(string(p))
Colin Crosse0df1a32019-06-09 19:40:08 -0700122 return len(p), nil
123}
124
Dan Willemsenb82471a2018-05-17 16:37:09 -0700125type errorLog struct {
Patrice Arruda297ceba2019-06-06 16:44:37 -0700126 w io.WriteCloser
Dan Willemsenb82471a2018-05-17 16:37:09 -0700127 empty bool
128}
129
130func NewErrorLog(log logger.Logger, filename string) StatusOutput {
131 f, err := logger.CreateFileWithRotation(filename, 5)
132 if err != nil {
133 log.Println("Failed to create error log file:", err)
134 return nil
135 }
136
137 return &errorLog{
138 w: f,
139 empty: true,
140 }
141}
142
143func (e *errorLog) StartAction(action *Action, counts Counts) {}
144
145func (e *errorLog) FinishAction(result ActionResult, counts Counts) {
146 if result.Error == nil {
147 return
148 }
149
Dan Willemsenb82471a2018-05-17 16:37:09 -0700150 if !e.empty {
151 fmt.Fprintf(e.w, "\n\n")
152 }
153 e.empty = false
154
155 fmt.Fprintf(e.w, "FAILED: %s\n", result.Description)
Patrice Arruda297ceba2019-06-06 16:44:37 -0700156
Dan Willemsenb82471a2018-05-17 16:37:09 -0700157 if len(result.Outputs) > 0 {
158 fmt.Fprintf(e.w, "Outputs: %s\n", strings.Join(result.Outputs, " "))
159 }
Patrice Arruda297ceba2019-06-06 16:44:37 -0700160
Dan Willemsenb82471a2018-05-17 16:37:09 -0700161 fmt.Fprintf(e.w, "Error: %s\n", result.Error)
162 if result.Command != "" {
163 fmt.Fprintf(e.w, "Command: %s\n", result.Command)
164 }
165 fmt.Fprintf(e.w, "Output:\n%s\n", result.Output)
166}
167
168func (e *errorLog) Flush() {
169 e.w.Close()
170}
171
172func (e *errorLog) Message(level MsgLevel, message string) {
173 if level < ErrorLvl {
174 return
175 }
176
177 if !e.empty {
178 fmt.Fprintf(e.w, "\n\n")
179 }
180 e.empty = false
181
182 fmt.Fprintf(e.w, "error: %s\n", message)
183}
Colin Crosse0df1a32019-06-09 19:40:08 -0700184
185func (e *errorLog) Write(p []byte) (int, error) {
186 fmt.Fprint(e.w, string(p))
187 return len(p), nil
188}
Patrice Arruda297ceba2019-06-06 16:44:37 -0700189
190type errorProtoLog struct {
191 errorProto soong_build_error_proto.BuildError
192 filename string
193 log logger.Logger
194}
195
196func NewProtoErrorLog(log logger.Logger, filename string) StatusOutput {
Patrice Arruda36b5e312020-03-11 12:41:47 -0700197 os.Remove(filename)
Patrice Arruda297ceba2019-06-06 16:44:37 -0700198 return &errorProtoLog{
199 errorProto: soong_build_error_proto.BuildError{},
200 filename: filename,
201 log: log,
202 }
203}
204
205func (e *errorProtoLog) StartAction(action *Action, counts Counts) {}
206
207func (e *errorProtoLog) FinishAction(result ActionResult, counts Counts) {
208 if result.Error == nil {
209 return
210 }
211
212 e.errorProto.ActionErrors = append(e.errorProto.ActionErrors, &soong_build_error_proto.BuildActionError{
213 Description: proto.String(result.Description),
214 Command: proto.String(result.Command),
215 Output: proto.String(result.Output),
216 Artifacts: result.Outputs,
217 Error: proto.String(result.Error.Error()),
218 })
Patrice Arruda297ceba2019-06-06 16:44:37 -0700219
Patrice Arruda85f227e2020-03-19 18:23:26 -0700220 err := writeToFile(&e.errorProto, e.filename)
Patrice Arruda297ceba2019-06-06 16:44:37 -0700221 if err != nil {
Colin Crossff27ce42019-11-26 16:20:03 -0800222 e.log.Printf("Failed to write file %s: %v\n", e.filename, err)
Patrice Arruda297ceba2019-06-06 16:44:37 -0700223 }
224}
225
Patrice Arruda85f227e2020-03-19 18:23:26 -0700226func (e *errorProtoLog) Flush() {
227 //Not required.
228}
229
Patrice Arruda297ceba2019-06-06 16:44:37 -0700230func (e *errorProtoLog) Message(level MsgLevel, message string) {
231 if level > ErrorLvl {
232 e.errorProto.ErrorMessages = append(e.errorProto.ErrorMessages, message)
233 }
234}
235
236func (e *errorProtoLog) Write(p []byte) (int, error) {
237 return 0, errors.New("not supported")
238}
Patrice Arruda74b43992020-03-11 08:21:05 -0700239
240type buildProgressLog struct {
241 filename string
242 log logger.Logger
243 failedActions uint64
244}
245
246func NewBuildProgressLog(log logger.Logger, filename string) StatusOutput {
247 return &buildProgressLog{
248 filename: filename,
249 log: log,
250 failedActions: 0,
251 }
252}
253
254func (b *buildProgressLog) StartAction(action *Action, counts Counts) {
255 b.updateCounters(counts)
256}
257
258func (b *buildProgressLog) FinishAction(result ActionResult, counts Counts) {
259 if result.Error != nil {
260 b.failedActions++
261 }
262 b.updateCounters(counts)
263}
264
265func (b *buildProgressLog) Flush() {
266 //Not required.
267}
268
269func (b *buildProgressLog) Message(level MsgLevel, message string) {
270 // Not required.
271}
272
273func (b *buildProgressLog) Write(p []byte) (int, error) {
274 return 0, errors.New("not supported")
275}
276
277func (b *buildProgressLog) updateCounters(counts Counts) {
278 err := writeToFile(
279 &soong_build_progress_proto.BuildProgress{
280 CurrentActions: proto.Uint64(uint64(counts.RunningActions)),
281 FinishedActions: proto.Uint64(uint64(counts.FinishedActions)),
282 TotalActions: proto.Uint64(uint64(counts.TotalActions)),
283 FailedActions: proto.Uint64(b.failedActions),
284 },
285 b.filename,
286 )
287 if err != nil {
288 b.log.Printf("Failed to write file %s: %v\n", b.filename, err)
289 }
290}
291
292func writeToFile(pb proto.Message, outputPath string) (err error) {
293 data, err := proto.Marshal(pb)
294 if err != nil {
295 return err
296 }
297
298 tempPath := outputPath + ".tmp"
299 err = ioutil.WriteFile(tempPath, []byte(data), 0644)
300 if err != nil {
301 return err
302 }
303
304 err = os.Rename(tempPath, outputPath)
305 if err != nil {
306 return err
307 }
308
309 return nil
310}