blob: 4e276416d4899da60e4179669e0b7c2acf676d9a [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 Arruda74b43992020-03-11 08:21:05 -070023 "os"
Dan Willemsenb82471a2018-05-17 16:37:09 -070024 "strings"
Patrice Arruda297ceba2019-06-06 16:44:37 -070025
26 "github.com/golang/protobuf/proto"
27
28 "android/soong/ui/logger"
29 "android/soong/ui/status/build_error_proto"
Patrice Arruda74b43992020-03-11 08:21:05 -070030 "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 {
159 return &errorProtoLog{
160 errorProto: soong_build_error_proto.BuildError{},
161 filename: filename,
162 log: log,
163 }
164}
165
166func (e *errorProtoLog) StartAction(action *Action, counts Counts) {}
167
168func (e *errorProtoLog) FinishAction(result ActionResult, counts Counts) {
169 if result.Error == nil {
170 return
171 }
172
173 e.errorProto.ActionErrors = append(e.errorProto.ActionErrors, &soong_build_error_proto.BuildActionError{
174 Description: proto.String(result.Description),
175 Command: proto.String(result.Command),
176 Output: proto.String(result.Output),
177 Artifacts: result.Outputs,
178 Error: proto.String(result.Error.Error()),
179 })
180}
181
182func (e *errorProtoLog) Flush() {
183 data, err := proto.Marshal(&e.errorProto)
184 if err != nil {
Colin Crossff27ce42019-11-26 16:20:03 -0800185 e.log.Printf("Failed to marshal build status proto: %v\n", err)
Patrice Arruda297ceba2019-06-06 16:44:37 -0700186 return
187 }
188 err = ioutil.WriteFile(e.filename, []byte(data), 0644)
189 if err != nil {
Colin Crossff27ce42019-11-26 16:20:03 -0800190 e.log.Printf("Failed to write file %s: %v\n", e.filename, err)
Patrice Arruda297ceba2019-06-06 16:44:37 -0700191 }
192}
193
194func (e *errorProtoLog) Message(level MsgLevel, message string) {
195 if level > ErrorLvl {
196 e.errorProto.ErrorMessages = append(e.errorProto.ErrorMessages, message)
197 }
198}
199
200func (e *errorProtoLog) Write(p []byte) (int, error) {
201 return 0, errors.New("not supported")
202}
Patrice Arruda74b43992020-03-11 08:21:05 -0700203
204type buildProgressLog struct {
205 filename string
206 log logger.Logger
207 failedActions uint64
208}
209
210func NewBuildProgressLog(log logger.Logger, filename string) StatusOutput {
211 return &buildProgressLog{
212 filename: filename,
213 log: log,
214 failedActions: 0,
215 }
216}
217
218func (b *buildProgressLog) StartAction(action *Action, counts Counts) {
219 b.updateCounters(counts)
220}
221
222func (b *buildProgressLog) FinishAction(result ActionResult, counts Counts) {
223 if result.Error != nil {
224 b.failedActions++
225 }
226 b.updateCounters(counts)
227}
228
229func (b *buildProgressLog) Flush() {
230 //Not required.
231}
232
233func (b *buildProgressLog) Message(level MsgLevel, message string) {
234 // Not required.
235}
236
237func (b *buildProgressLog) Write(p []byte) (int, error) {
238 return 0, errors.New("not supported")
239}
240
241func (b *buildProgressLog) updateCounters(counts Counts) {
242 err := writeToFile(
243 &soong_build_progress_proto.BuildProgress{
244 CurrentActions: proto.Uint64(uint64(counts.RunningActions)),
245 FinishedActions: proto.Uint64(uint64(counts.FinishedActions)),
246 TotalActions: proto.Uint64(uint64(counts.TotalActions)),
247 FailedActions: proto.Uint64(b.failedActions),
248 },
249 b.filename,
250 )
251 if err != nil {
252 b.log.Printf("Failed to write file %s: %v\n", b.filename, err)
253 }
254}
255
256func writeToFile(pb proto.Message, outputPath string) (err error) {
257 data, err := proto.Marshal(pb)
258 if err != nil {
259 return err
260 }
261
262 tempPath := outputPath + ".tmp"
263 err = ioutil.WriteFile(tempPath, []byte(data), 0644)
264 if err != nil {
265 return err
266 }
267
268 err = os.Rename(tempPath, outputPath)
269 if err != nil {
270 return err
271 }
272
273 return nil
274}