1*333d2b36SAndroid Build Coastguard Worker// Copyright 2018 Google Inc. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage status 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "bufio" 19*333d2b36SAndroid Build Coastguard Worker "fmt" 20*333d2b36SAndroid Build Coastguard Worker "io" 21*333d2b36SAndroid Build Coastguard Worker "os" 22*333d2b36SAndroid Build Coastguard Worker "regexp" 23*333d2b36SAndroid Build Coastguard Worker "runtime" 24*333d2b36SAndroid Build Coastguard Worker "strings" 25*333d2b36SAndroid Build Coastguard Worker "syscall" 26*333d2b36SAndroid Build Coastguard Worker "time" 27*333d2b36SAndroid Build Coastguard Worker 28*333d2b36SAndroid Build Coastguard Worker "google.golang.org/protobuf/proto" 29*333d2b36SAndroid Build Coastguard Worker 30*333d2b36SAndroid Build Coastguard Worker "android/soong/ui/logger" 31*333d2b36SAndroid Build Coastguard Worker "android/soong/ui/status/ninja_frontend" 32*333d2b36SAndroid Build Coastguard Worker) 33*333d2b36SAndroid Build Coastguard Worker 34*333d2b36SAndroid Build Coastguard Worker// NewNinjaReader reads the protobuf frontend format from ninja and translates it 35*333d2b36SAndroid Build Coastguard Worker// into calls on the ToolStatus API. 36*333d2b36SAndroid Build Coastguard Workerfunc NewNinjaReader(ctx logger.Logger, status ToolStatus, fifo string) *NinjaReader { 37*333d2b36SAndroid Build Coastguard Worker os.Remove(fifo) 38*333d2b36SAndroid Build Coastguard Worker 39*333d2b36SAndroid Build Coastguard Worker if err := syscall.Mkfifo(fifo, 0666); err != nil { 40*333d2b36SAndroid Build Coastguard Worker ctx.Fatalf("Failed to mkfifo(%q): %v", fifo, err) 41*333d2b36SAndroid Build Coastguard Worker } 42*333d2b36SAndroid Build Coastguard Worker 43*333d2b36SAndroid Build Coastguard Worker n := &NinjaReader{ 44*333d2b36SAndroid Build Coastguard Worker status: status, 45*333d2b36SAndroid Build Coastguard Worker fifo: fifo, 46*333d2b36SAndroid Build Coastguard Worker forceClose: make(chan bool), 47*333d2b36SAndroid Build Coastguard Worker done: make(chan bool), 48*333d2b36SAndroid Build Coastguard Worker cancelOpen: make(chan bool), 49*333d2b36SAndroid Build Coastguard Worker } 50*333d2b36SAndroid Build Coastguard Worker 51*333d2b36SAndroid Build Coastguard Worker go n.run() 52*333d2b36SAndroid Build Coastguard Worker 53*333d2b36SAndroid Build Coastguard Worker return n 54*333d2b36SAndroid Build Coastguard Worker} 55*333d2b36SAndroid Build Coastguard Worker 56*333d2b36SAndroid Build Coastguard Workertype NinjaReader struct { 57*333d2b36SAndroid Build Coastguard Worker status ToolStatus 58*333d2b36SAndroid Build Coastguard Worker fifo string 59*333d2b36SAndroid Build Coastguard Worker forceClose chan bool 60*333d2b36SAndroid Build Coastguard Worker done chan bool 61*333d2b36SAndroid Build Coastguard Worker cancelOpen chan bool 62*333d2b36SAndroid Build Coastguard Worker} 63*333d2b36SAndroid Build Coastguard Worker 64*333d2b36SAndroid Build Coastguard Workerconst NINJA_READER_CLOSE_TIMEOUT = 5 * time.Second 65*333d2b36SAndroid Build Coastguard Worker 66*333d2b36SAndroid Build Coastguard Worker// Close waits for NinjaReader to finish reading from the fifo, or 5 seconds. 67*333d2b36SAndroid Build Coastguard Workerfunc (n *NinjaReader) Close() { 68*333d2b36SAndroid Build Coastguard Worker // Signal the goroutine to stop if it is blocking opening the fifo. 69*333d2b36SAndroid Build Coastguard Worker close(n.cancelOpen) 70*333d2b36SAndroid Build Coastguard Worker 71*333d2b36SAndroid Build Coastguard Worker // Ninja should already have exited or been killed, wait 5 seconds for the FIFO to be closed and any 72*333d2b36SAndroid Build Coastguard Worker // remaining messages to be processed through the NinjaReader.run goroutine. 73*333d2b36SAndroid Build Coastguard Worker timeoutCh := time.After(NINJA_READER_CLOSE_TIMEOUT) 74*333d2b36SAndroid Build Coastguard Worker select { 75*333d2b36SAndroid Build Coastguard Worker case <-n.done: 76*333d2b36SAndroid Build Coastguard Worker return 77*333d2b36SAndroid Build Coastguard Worker case <-timeoutCh: 78*333d2b36SAndroid Build Coastguard Worker // Channel is not closed yet 79*333d2b36SAndroid Build Coastguard Worker } 80*333d2b36SAndroid Build Coastguard Worker 81*333d2b36SAndroid Build Coastguard Worker n.status.Error(fmt.Sprintf("ninja fifo didn't finish after %s", NINJA_READER_CLOSE_TIMEOUT.String())) 82*333d2b36SAndroid Build Coastguard Worker 83*333d2b36SAndroid Build Coastguard Worker // Force close the reader even if the FIFO didn't close. 84*333d2b36SAndroid Build Coastguard Worker close(n.forceClose) 85*333d2b36SAndroid Build Coastguard Worker 86*333d2b36SAndroid Build Coastguard Worker // Wait again for the reader thread to acknowledge the close before giving up and assuming it isn't going 87*333d2b36SAndroid Build Coastguard Worker // to send anything else. 88*333d2b36SAndroid Build Coastguard Worker timeoutCh = time.After(NINJA_READER_CLOSE_TIMEOUT) 89*333d2b36SAndroid Build Coastguard Worker select { 90*333d2b36SAndroid Build Coastguard Worker case <-n.done: 91*333d2b36SAndroid Build Coastguard Worker return 92*333d2b36SAndroid Build Coastguard Worker case <-timeoutCh: 93*333d2b36SAndroid Build Coastguard Worker // Channel is not closed yet 94*333d2b36SAndroid Build Coastguard Worker } 95*333d2b36SAndroid Build Coastguard Worker 96*333d2b36SAndroid Build Coastguard Worker n.status.Verbose(fmt.Sprintf("ninja fifo didn't finish even after force closing after %s", NINJA_READER_CLOSE_TIMEOUT.String())) 97*333d2b36SAndroid Build Coastguard Worker} 98*333d2b36SAndroid Build Coastguard Worker 99*333d2b36SAndroid Build Coastguard Workerfunc (n *NinjaReader) run() { 100*333d2b36SAndroid Build Coastguard Worker defer close(n.done) 101*333d2b36SAndroid Build Coastguard Worker 102*333d2b36SAndroid Build Coastguard Worker // Opening the fifo can block forever if ninja never opens the write end, do it in a goroutine so this 103*333d2b36SAndroid Build Coastguard Worker // method can exit on cancel. 104*333d2b36SAndroid Build Coastguard Worker fileCh := make(chan *os.File) 105*333d2b36SAndroid Build Coastguard Worker go func() { 106*333d2b36SAndroid Build Coastguard Worker f, err := os.Open(n.fifo) 107*333d2b36SAndroid Build Coastguard Worker if err != nil { 108*333d2b36SAndroid Build Coastguard Worker n.status.Error(fmt.Sprintf("Failed to open fifo: %v", err)) 109*333d2b36SAndroid Build Coastguard Worker close(fileCh) 110*333d2b36SAndroid Build Coastguard Worker return 111*333d2b36SAndroid Build Coastguard Worker } 112*333d2b36SAndroid Build Coastguard Worker fileCh <- f 113*333d2b36SAndroid Build Coastguard Worker }() 114*333d2b36SAndroid Build Coastguard Worker 115*333d2b36SAndroid Build Coastguard Worker var f *os.File 116*333d2b36SAndroid Build Coastguard Worker 117*333d2b36SAndroid Build Coastguard Worker select { 118*333d2b36SAndroid Build Coastguard Worker case f = <-fileCh: 119*333d2b36SAndroid Build Coastguard Worker // Nothing 120*333d2b36SAndroid Build Coastguard Worker case <-n.cancelOpen: 121*333d2b36SAndroid Build Coastguard Worker return 122*333d2b36SAndroid Build Coastguard Worker } 123*333d2b36SAndroid Build Coastguard Worker 124*333d2b36SAndroid Build Coastguard Worker defer f.Close() 125*333d2b36SAndroid Build Coastguard Worker 126*333d2b36SAndroid Build Coastguard Worker r := bufio.NewReader(f) 127*333d2b36SAndroid Build Coastguard Worker 128*333d2b36SAndroid Build Coastguard Worker running := map[uint32]*Action{} 129*333d2b36SAndroid Build Coastguard Worker 130*333d2b36SAndroid Build Coastguard Worker msgChan := make(chan *ninja_frontend.Status) 131*333d2b36SAndroid Build Coastguard Worker 132*333d2b36SAndroid Build Coastguard Worker // Read from the ninja fifo and decode the protobuf in a goroutine so the main NinjaReader.run goroutine 133*333d2b36SAndroid Build Coastguard Worker // can listen 134*333d2b36SAndroid Build Coastguard Worker go func() { 135*333d2b36SAndroid Build Coastguard Worker defer close(msgChan) 136*333d2b36SAndroid Build Coastguard Worker for { 137*333d2b36SAndroid Build Coastguard Worker size, err := readVarInt(r) 138*333d2b36SAndroid Build Coastguard Worker if err != nil { 139*333d2b36SAndroid Build Coastguard Worker if err != io.EOF { 140*333d2b36SAndroid Build Coastguard Worker n.status.Error(fmt.Sprintf("Got error reading from ninja: %s", err)) 141*333d2b36SAndroid Build Coastguard Worker } 142*333d2b36SAndroid Build Coastguard Worker return 143*333d2b36SAndroid Build Coastguard Worker } 144*333d2b36SAndroid Build Coastguard Worker 145*333d2b36SAndroid Build Coastguard Worker buf := make([]byte, size) 146*333d2b36SAndroid Build Coastguard Worker _, err = io.ReadFull(r, buf) 147*333d2b36SAndroid Build Coastguard Worker if err != nil { 148*333d2b36SAndroid Build Coastguard Worker if err == io.EOF { 149*333d2b36SAndroid Build Coastguard Worker n.status.Print(fmt.Sprintf("Missing message of size %d from ninja\n", size)) 150*333d2b36SAndroid Build Coastguard Worker } else { 151*333d2b36SAndroid Build Coastguard Worker n.status.Error(fmt.Sprintf("Got error reading from ninja: %s", err)) 152*333d2b36SAndroid Build Coastguard Worker } 153*333d2b36SAndroid Build Coastguard Worker return 154*333d2b36SAndroid Build Coastguard Worker } 155*333d2b36SAndroid Build Coastguard Worker 156*333d2b36SAndroid Build Coastguard Worker msg := &ninja_frontend.Status{} 157*333d2b36SAndroid Build Coastguard Worker err = proto.Unmarshal(buf, msg) 158*333d2b36SAndroid Build Coastguard Worker if err != nil { 159*333d2b36SAndroid Build Coastguard Worker n.status.Print(fmt.Sprintf("Error reading message from ninja: %v", err)) 160*333d2b36SAndroid Build Coastguard Worker continue 161*333d2b36SAndroid Build Coastguard Worker } 162*333d2b36SAndroid Build Coastguard Worker 163*333d2b36SAndroid Build Coastguard Worker msgChan <- msg 164*333d2b36SAndroid Build Coastguard Worker } 165*333d2b36SAndroid Build Coastguard Worker }() 166*333d2b36SAndroid Build Coastguard Worker 167*333d2b36SAndroid Build Coastguard Worker for { 168*333d2b36SAndroid Build Coastguard Worker var msg *ninja_frontend.Status 169*333d2b36SAndroid Build Coastguard Worker var msgOk bool 170*333d2b36SAndroid Build Coastguard Worker select { 171*333d2b36SAndroid Build Coastguard Worker case <-n.forceClose: 172*333d2b36SAndroid Build Coastguard Worker // Close() has been called, but the reader goroutine didn't get EOF after 5 seconds 173*333d2b36SAndroid Build Coastguard Worker break 174*333d2b36SAndroid Build Coastguard Worker case msg, msgOk = <-msgChan: 175*333d2b36SAndroid Build Coastguard Worker // msg is ready or closed 176*333d2b36SAndroid Build Coastguard Worker } 177*333d2b36SAndroid Build Coastguard Worker 178*333d2b36SAndroid Build Coastguard Worker if !msgOk { 179*333d2b36SAndroid Build Coastguard Worker // msgChan is closed 180*333d2b36SAndroid Build Coastguard Worker break 181*333d2b36SAndroid Build Coastguard Worker } 182*333d2b36SAndroid Build Coastguard Worker 183*333d2b36SAndroid Build Coastguard Worker if msg.BuildStarted != nil { 184*333d2b36SAndroid Build Coastguard Worker parallelism := uint32(runtime.NumCPU()) 185*333d2b36SAndroid Build Coastguard Worker if msg.BuildStarted.GetParallelism() > 0 { 186*333d2b36SAndroid Build Coastguard Worker parallelism = msg.BuildStarted.GetParallelism() 187*333d2b36SAndroid Build Coastguard Worker } 188*333d2b36SAndroid Build Coastguard Worker // It is estimated from total time / parallelism assumming the build is packing enough. 189*333d2b36SAndroid Build Coastguard Worker estimatedDurationFromTotal := time.Duration(msg.BuildStarted.GetEstimatedTotalTime()/parallelism) * time.Millisecond 190*333d2b36SAndroid Build Coastguard Worker // It is estimated from critical path time which is useful for small size build. 191*333d2b36SAndroid Build Coastguard Worker estimatedDurationFromCriticalPath := time.Duration(msg.BuildStarted.GetCriticalPathTime()) * time.Millisecond 192*333d2b36SAndroid Build Coastguard Worker // Select the longer one. 193*333d2b36SAndroid Build Coastguard Worker estimatedDuration := max(estimatedDurationFromTotal, estimatedDurationFromCriticalPath) 194*333d2b36SAndroid Build Coastguard Worker 195*333d2b36SAndroid Build Coastguard Worker if estimatedDuration > 0 { 196*333d2b36SAndroid Build Coastguard Worker n.status.SetEstimatedTime(time.Now().Add(estimatedDuration)) 197*333d2b36SAndroid Build Coastguard Worker n.status.Verbose(fmt.Sprintf("parallelism: %d, estimated from total time: %s, critical path time: %s", 198*333d2b36SAndroid Build Coastguard Worker parallelism, 199*333d2b36SAndroid Build Coastguard Worker estimatedDurationFromTotal, 200*333d2b36SAndroid Build Coastguard Worker estimatedDurationFromCriticalPath)) 201*333d2b36SAndroid Build Coastguard Worker 202*333d2b36SAndroid Build Coastguard Worker } 203*333d2b36SAndroid Build Coastguard Worker } 204*333d2b36SAndroid Build Coastguard Worker if msg.TotalEdges != nil { 205*333d2b36SAndroid Build Coastguard Worker n.status.SetTotalActions(int(msg.TotalEdges.GetTotalEdges())) 206*333d2b36SAndroid Build Coastguard Worker } 207*333d2b36SAndroid Build Coastguard Worker if msg.EdgeStarted != nil { 208*333d2b36SAndroid Build Coastguard Worker action := &Action{ 209*333d2b36SAndroid Build Coastguard Worker Description: msg.EdgeStarted.GetDesc(), 210*333d2b36SAndroid Build Coastguard Worker Outputs: msg.EdgeStarted.Outputs, 211*333d2b36SAndroid Build Coastguard Worker Inputs: msg.EdgeStarted.Inputs, 212*333d2b36SAndroid Build Coastguard Worker Command: msg.EdgeStarted.GetCommand(), 213*333d2b36SAndroid Build Coastguard Worker ChangedInputs: msg.EdgeStarted.ChangedInputs, 214*333d2b36SAndroid Build Coastguard Worker } 215*333d2b36SAndroid Build Coastguard Worker n.status.StartAction(action) 216*333d2b36SAndroid Build Coastguard Worker running[msg.EdgeStarted.GetId()] = action 217*333d2b36SAndroid Build Coastguard Worker } 218*333d2b36SAndroid Build Coastguard Worker if msg.EdgeFinished != nil { 219*333d2b36SAndroid Build Coastguard Worker if started, ok := running[msg.EdgeFinished.GetId()]; ok { 220*333d2b36SAndroid Build Coastguard Worker delete(running, msg.EdgeFinished.GetId()) 221*333d2b36SAndroid Build Coastguard Worker 222*333d2b36SAndroid Build Coastguard Worker var err error 223*333d2b36SAndroid Build Coastguard Worker exitCode := int(msg.EdgeFinished.GetStatus()) 224*333d2b36SAndroid Build Coastguard Worker if exitCode != 0 { 225*333d2b36SAndroid Build Coastguard Worker err = fmt.Errorf("exited with code: %d", exitCode) 226*333d2b36SAndroid Build Coastguard Worker } 227*333d2b36SAndroid Build Coastguard Worker 228*333d2b36SAndroid Build Coastguard Worker outputWithErrorHint := errorHintGenerator.GetOutputWithErrorHint(msg.EdgeFinished.GetOutput(), exitCode) 229*333d2b36SAndroid Build Coastguard Worker n.status.FinishAction(ActionResult{ 230*333d2b36SAndroid Build Coastguard Worker Action: started, 231*333d2b36SAndroid Build Coastguard Worker Output: outputWithErrorHint, 232*333d2b36SAndroid Build Coastguard Worker Error: err, 233*333d2b36SAndroid Build Coastguard Worker Stats: ActionResultStats{ 234*333d2b36SAndroid Build Coastguard Worker UserTime: msg.EdgeFinished.GetUserTime(), 235*333d2b36SAndroid Build Coastguard Worker SystemTime: msg.EdgeFinished.GetSystemTime(), 236*333d2b36SAndroid Build Coastguard Worker MaxRssKB: msg.EdgeFinished.GetMaxRssKb(), 237*333d2b36SAndroid Build Coastguard Worker MinorPageFaults: msg.EdgeFinished.GetMinorPageFaults(), 238*333d2b36SAndroid Build Coastguard Worker MajorPageFaults: msg.EdgeFinished.GetMajorPageFaults(), 239*333d2b36SAndroid Build Coastguard Worker IOInputKB: msg.EdgeFinished.GetIoInputKb(), 240*333d2b36SAndroid Build Coastguard Worker IOOutputKB: msg.EdgeFinished.GetIoOutputKb(), 241*333d2b36SAndroid Build Coastguard Worker VoluntaryContextSwitches: msg.EdgeFinished.GetVoluntaryContextSwitches(), 242*333d2b36SAndroid Build Coastguard Worker InvoluntaryContextSwitches: msg.EdgeFinished.GetInvoluntaryContextSwitches(), 243*333d2b36SAndroid Build Coastguard Worker Tags: msg.EdgeFinished.GetTags(), 244*333d2b36SAndroid Build Coastguard Worker }, 245*333d2b36SAndroid Build Coastguard Worker }) 246*333d2b36SAndroid Build Coastguard Worker } 247*333d2b36SAndroid Build Coastguard Worker } 248*333d2b36SAndroid Build Coastguard Worker if msg.Message != nil { 249*333d2b36SAndroid Build Coastguard Worker message := "ninja: " + msg.Message.GetMessage() 250*333d2b36SAndroid Build Coastguard Worker switch msg.Message.GetLevel() { 251*333d2b36SAndroid Build Coastguard Worker case ninja_frontend.Status_Message_INFO: 252*333d2b36SAndroid Build Coastguard Worker n.status.Status(message) 253*333d2b36SAndroid Build Coastguard Worker case ninja_frontend.Status_Message_WARNING: 254*333d2b36SAndroid Build Coastguard Worker n.status.Print("warning: " + message) 255*333d2b36SAndroid Build Coastguard Worker case ninja_frontend.Status_Message_ERROR: 256*333d2b36SAndroid Build Coastguard Worker n.status.Error(message) 257*333d2b36SAndroid Build Coastguard Worker case ninja_frontend.Status_Message_DEBUG: 258*333d2b36SAndroid Build Coastguard Worker n.status.Verbose(message) 259*333d2b36SAndroid Build Coastguard Worker default: 260*333d2b36SAndroid Build Coastguard Worker n.status.Print(message) 261*333d2b36SAndroid Build Coastguard Worker } 262*333d2b36SAndroid Build Coastguard Worker } 263*333d2b36SAndroid Build Coastguard Worker if msg.BuildFinished != nil { 264*333d2b36SAndroid Build Coastguard Worker n.status.Finish() 265*333d2b36SAndroid Build Coastguard Worker } 266*333d2b36SAndroid Build Coastguard Worker } 267*333d2b36SAndroid Build Coastguard Worker} 268*333d2b36SAndroid Build Coastguard Worker 269*333d2b36SAndroid Build Coastguard Workerfunc readVarInt(r *bufio.Reader) (int, error) { 270*333d2b36SAndroid Build Coastguard Worker ret := 0 271*333d2b36SAndroid Build Coastguard Worker shift := uint(0) 272*333d2b36SAndroid Build Coastguard Worker 273*333d2b36SAndroid Build Coastguard Worker for { 274*333d2b36SAndroid Build Coastguard Worker b, err := r.ReadByte() 275*333d2b36SAndroid Build Coastguard Worker if err != nil { 276*333d2b36SAndroid Build Coastguard Worker return 0, err 277*333d2b36SAndroid Build Coastguard Worker } 278*333d2b36SAndroid Build Coastguard Worker 279*333d2b36SAndroid Build Coastguard Worker ret += int(b&0x7f) << (shift * 7) 280*333d2b36SAndroid Build Coastguard Worker if b&0x80 == 0 { 281*333d2b36SAndroid Build Coastguard Worker break 282*333d2b36SAndroid Build Coastguard Worker } 283*333d2b36SAndroid Build Coastguard Worker shift += 1 284*333d2b36SAndroid Build Coastguard Worker if shift > 4 { 285*333d2b36SAndroid Build Coastguard Worker return 0, fmt.Errorf("Expected varint32 length-delimited message") 286*333d2b36SAndroid Build Coastguard Worker } 287*333d2b36SAndroid Build Coastguard Worker } 288*333d2b36SAndroid Build Coastguard Worker 289*333d2b36SAndroid Build Coastguard Worker return ret, nil 290*333d2b36SAndroid Build Coastguard Worker} 291*333d2b36SAndroid Build Coastguard Worker 292*333d2b36SAndroid Build Coastguard Worker// key is pattern in stdout/stderr 293*333d2b36SAndroid Build Coastguard Worker// value is error hint 294*333d2b36SAndroid Build Coastguard Workervar allErrorHints = map[string]string{ 295*333d2b36SAndroid Build Coastguard Worker "Read-only file system": `\nWrite to a read-only file system detected. Possible fixes include 296*333d2b36SAndroid Build Coastguard Worker1. Generate file directly to out/ which is ReadWrite, #recommend solution 297*333d2b36SAndroid Build Coastguard Worker2. BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST := <my/path/1> <my/path/2> #discouraged, subset of source tree will be RW 298*333d2b36SAndroid Build Coastguard Worker3. BUILD_BROKEN_SRC_DIR_IS_WRITABLE := true #highly discouraged, entire source tree will be RW 299*333d2b36SAndroid Build Coastguard Worker`, 300*333d2b36SAndroid Build Coastguard Worker} 301*333d2b36SAndroid Build Coastguard Workervar errorHintGenerator = *newErrorHintGenerator(allErrorHints) 302*333d2b36SAndroid Build Coastguard Worker 303*333d2b36SAndroid Build Coastguard Workertype ErrorHintGenerator struct { 304*333d2b36SAndroid Build Coastguard Worker allErrorHints map[string]string 305*333d2b36SAndroid Build Coastguard Worker allErrorHintPatternsCompiled *regexp.Regexp 306*333d2b36SAndroid Build Coastguard Worker} 307*333d2b36SAndroid Build Coastguard Worker 308*333d2b36SAndroid Build Coastguard Workerfunc newErrorHintGenerator(allErrorHints map[string]string) *ErrorHintGenerator { 309*333d2b36SAndroid Build Coastguard Worker var allErrorHintPatterns []string 310*333d2b36SAndroid Build Coastguard Worker for errorHintPattern, _ := range allErrorHints { 311*333d2b36SAndroid Build Coastguard Worker allErrorHintPatterns = append(allErrorHintPatterns, errorHintPattern) 312*333d2b36SAndroid Build Coastguard Worker } 313*333d2b36SAndroid Build Coastguard Worker allErrorHintPatternsRegex := strings.Join(allErrorHintPatterns[:], "|") 314*333d2b36SAndroid Build Coastguard Worker re := regexp.MustCompile(allErrorHintPatternsRegex) 315*333d2b36SAndroid Build Coastguard Worker return &ErrorHintGenerator{ 316*333d2b36SAndroid Build Coastguard Worker allErrorHints: allErrorHints, 317*333d2b36SAndroid Build Coastguard Worker allErrorHintPatternsCompiled: re, 318*333d2b36SAndroid Build Coastguard Worker } 319*333d2b36SAndroid Build Coastguard Worker} 320*333d2b36SAndroid Build Coastguard Worker 321*333d2b36SAndroid Build Coastguard Workerfunc (errorHintGenerator *ErrorHintGenerator) GetOutputWithErrorHint(rawOutput string, buildExitCode int) string { 322*333d2b36SAndroid Build Coastguard Worker if buildExitCode == 0 { 323*333d2b36SAndroid Build Coastguard Worker return rawOutput 324*333d2b36SAndroid Build Coastguard Worker } 325*333d2b36SAndroid Build Coastguard Worker errorHint := errorHintGenerator.getErrorHint(rawOutput) 326*333d2b36SAndroid Build Coastguard Worker if errorHint == nil { 327*333d2b36SAndroid Build Coastguard Worker return rawOutput 328*333d2b36SAndroid Build Coastguard Worker } 329*333d2b36SAndroid Build Coastguard Worker return rawOutput + *errorHint 330*333d2b36SAndroid Build Coastguard Worker} 331*333d2b36SAndroid Build Coastguard Worker 332*333d2b36SAndroid Build Coastguard Worker// Returns the error hint corresponding to the FIRST match in raw output 333*333d2b36SAndroid Build Coastguard Workerfunc (errorHintGenerator *ErrorHintGenerator) getErrorHint(rawOutput string) *string { 334*333d2b36SAndroid Build Coastguard Worker firstMatch := errorHintGenerator.allErrorHintPatternsCompiled.FindString(rawOutput) 335*333d2b36SAndroid Build Coastguard Worker if _, found := errorHintGenerator.allErrorHints[firstMatch]; found { 336*333d2b36SAndroid Build Coastguard Worker errorHint := errorHintGenerator.allErrorHints[firstMatch] 337*333d2b36SAndroid Build Coastguard Worker return &errorHint 338*333d2b36SAndroid Build Coastguard Worker } 339*333d2b36SAndroid Build Coastguard Worker return nil 340*333d2b36SAndroid Build Coastguard Worker} 341