blob: 1485c8dd871ab0e9b05377be3695241ea1836631 [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 (
18 "bufio"
19 "fmt"
20 "io"
21 "regexp"
22 "strconv"
23 "strings"
24)
25
26var katiError = regexp.MustCompile(`^(\033\[1m)?[^ ]+:[0-9]+: (\033\[31m)?error:`)
Dan Willemsenfb1271a2018-09-26 15:00:42 -070027var katiIncludeRe = regexp.MustCompile(`^(\[(\d+)/(\d+)] )?((including [^ ]+|initializing (build|packaging) system|finishing (build|packaging) rules|writing (build|packaging) rules) ...)$`)
Dan Willemsenb82471a2018-05-17 16:37:09 -070028var katiLogRe = regexp.MustCompile(`^\*kati\*: `)
29var katiNinjaMissing = regexp.MustCompile("^[^ ]+ is missing, regenerating...$")
30
31type katiOutputParser struct {
32 st ToolStatus
33
34 count int
35 total int
36 extra int
37
38 action *Action
39 buf strings.Builder
40 hasError bool
41}
42
43func (k *katiOutputParser) flushAction() {
44 if k.action == nil {
45 return
46 }
47
48 var err error
49 if k.hasError {
50 err = fmt.Errorf("makefile error")
51 }
52
53 k.st.FinishAction(ActionResult{
54 Action: k.action,
55 Output: k.buf.String(),
56 Error: err,
57 })
58
59 k.buf.Reset()
60 k.hasError = false
61}
62
63func (k *katiOutputParser) parseLine(line string) {
64 // Only put kati debug/stat lines in our verbose log
65 if katiLogRe.MatchString(line) {
66 k.st.Verbose(line)
67 return
68 }
69
70 if matches := katiIncludeRe.FindStringSubmatch(line); len(matches) > 0 {
71 k.flushAction()
72 k.count += 1
73
74 matches := katiIncludeRe.FindStringSubmatch(line)
75 if matches[2] != "" {
76 idx, err := strconv.Atoi(matches[2])
77
78 if err == nil && idx+k.extra != k.count {
79 k.extra = k.count - idx
80 k.st.SetTotalActions(k.total + k.extra)
81 }
82 } else {
83 k.extra += 1
84 k.st.SetTotalActions(k.total + k.extra)
85 }
86
87 if matches[3] != "" {
88 tot, err := strconv.Atoi(matches[3])
89
90 if err == nil && tot != k.total {
91 k.total = tot
92 k.st.SetTotalActions(k.total + k.extra)
93 }
94 }
95
96 k.action = &Action{
97 Description: matches[4],
98 }
99 k.st.StartAction(k.action)
100 } else if k.action != nil {
101 if katiError.MatchString(line) {
102 k.hasError = true
103 }
104 k.buf.WriteString(line)
105 k.buf.WriteString("\n")
106 } else {
107 // Before we've started executing actions from Kati
108 if line == "No need to regenerate ninja file" || katiNinjaMissing.MatchString(line) {
109 k.st.Status(line)
110 } else {
111 k.st.Print(line)
112 }
113 }
114}
115
116// KatiReader reads the output from Kati, and turns it into Actions and
117// messages that are passed into the ToolStatus API.
118func KatiReader(st ToolStatus, pipe io.ReadCloser) {
119 parser := &katiOutputParser{
120 st: st,
121 }
122
123 scanner := bufio.NewScanner(pipe)
Colin Cross75ec1ec2019-01-30 14:20:45 -0800124 scanner.Buffer(nil, 2*1024*1024)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700125 for scanner.Scan() {
126 parser.parseLine(scanner.Text())
127 }
128
129 parser.flushAction()
130
131 if err := scanner.Err(); err != nil {
132 var buf strings.Builder
133 io.Copy(&buf, pipe)
134 st.Print(fmt.Sprintf("Error from kati parser: %s", err))
135 st.Print(buf.String())
136 }
137
138 st.Finish()
139}