• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 package status
16 
17 import (
18 	"compress/gzip"
19 	"errors"
20 	"fmt"
21 	"io"
22 	"io/ioutil"
23 	"os"
24 	"strings"
25 
26 	"github.com/golang/protobuf/proto"
27 
28 	"android/soong/ui/logger"
29 	"android/soong/ui/status/build_error_proto"
30 	"android/soong/ui/status/build_progress_proto"
31 )
32 
33 type verboseLog struct {
34 	w io.WriteCloser
35 }
36 
37 func 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 
55 func (v *verboseLog) StartAction(action *Action, counts Counts) {}
56 
57 func (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 
74 func (v *verboseLog) Flush() {
75 	v.w.Close()
76 }
77 
78 func (v *verboseLog) Message(level MsgLevel, message string) {
79 	fmt.Fprintf(v.w, "%s%s\n", level.Prefix(), message)
80 }
81 
82 func (v *verboseLog) Write(p []byte) (int, error) {
83 	fmt.Fprint(v.w, string(p))
84 	return len(p), nil
85 }
86 
87 type errorLog struct {
88 	w     io.WriteCloser
89 	empty bool
90 }
91 
92 func 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 
105 func (e *errorLog) StartAction(action *Action, counts Counts) {}
106 
107 func (e *errorLog) FinishAction(result ActionResult, counts Counts) {
108 	if result.Error == nil {
109 		return
110 	}
111 
112 	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)
118 
119 	if len(result.Outputs) > 0 {
120 		fmt.Fprintf(e.w, "Outputs: %s\n", strings.Join(result.Outputs, " "))
121 	}
122 
123 	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 
130 func (e *errorLog) Flush() {
131 	e.w.Close()
132 }
133 
134 func (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 }
146 
147 func (e *errorLog) Write(p []byte) (int, error) {
148 	fmt.Fprint(e.w, string(p))
149 	return len(p), nil
150 }
151 
152 type errorProtoLog struct {
153 	errorProto soong_build_error_proto.BuildError
154 	filename   string
155 	log        logger.Logger
156 }
157 
158 func NewProtoErrorLog(log logger.Logger, filename string) StatusOutput {
159 	os.Remove(filename)
160 	return &errorProtoLog{
161 		errorProto: soong_build_error_proto.BuildError{},
162 		filename:   filename,
163 		log:        log,
164 	}
165 }
166 
167 func (e *errorProtoLog) StartAction(action *Action, counts Counts) {}
168 
169 func (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 	})
181 
182 	err := writeToFile(&e.errorProto, e.filename)
183 	if err != nil {
184 		e.log.Printf("Failed to write file %s: %v\n", e.filename, err)
185 	}
186 }
187 
188 func (e *errorProtoLog) Flush() {
189 	//Not required.
190 }
191 
192 func (e *errorProtoLog) Message(level MsgLevel, message string) {
193 	if level > ErrorLvl {
194 		e.errorProto.ErrorMessages = append(e.errorProto.ErrorMessages, message)
195 	}
196 }
197 
198 func (e *errorProtoLog) Write(p []byte) (int, error) {
199 	return 0, errors.New("not supported")
200 }
201 
202 type buildProgressLog struct {
203 	filename      string
204 	log           logger.Logger
205 	failedActions uint64
206 }
207 
208 func NewBuildProgressLog(log logger.Logger, filename string) StatusOutput {
209 	return &buildProgressLog{
210 		filename:      filename,
211 		log:           log,
212 		failedActions: 0,
213 	}
214 }
215 
216 func (b *buildProgressLog) StartAction(action *Action, counts Counts) {
217 	b.updateCounters(counts)
218 }
219 
220 func (b *buildProgressLog) FinishAction(result ActionResult, counts Counts) {
221 	if result.Error != nil {
222 		b.failedActions++
223 	}
224 	b.updateCounters(counts)
225 }
226 
227 func (b *buildProgressLog) Flush() {
228 	//Not required.
229 }
230 
231 func (b *buildProgressLog) Message(level MsgLevel, message string) {
232 	// Not required.
233 }
234 
235 func (b *buildProgressLog) Write(p []byte) (int, error) {
236 	return 0, errors.New("not supported")
237 }
238 
239 func (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 
254 func 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 }
273