github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/soong/cmd/javac_wrapper/javac_wrapper.go (about) 1 // Copyright 2017 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 // soong_javac_wrapper expects a javac command line and argments, executes 16 // it, and produces an ANSI colorized version of the output on stdout. 17 // 18 // It also hides the unhelpful and unhideable "warning there is a warning" 19 // messages. 20 // 21 // Each javac build statement has an order-only dependency on the 22 // soong_javac_wrapper tool, which means the javac command will not be rerun 23 // if soong_javac_wrapper changes. That means that soong_javac_wrapper must 24 // not do anything that will affect the results of the build. 25 package main 26 27 import ( 28 "bufio" 29 "fmt" 30 "io" 31 "os" 32 "os/exec" 33 "regexp" 34 "syscall" 35 ) 36 37 // Regular expressions are based on 38 // https://chromium.googlesource.com/chromium/src/+/master/build/android/gyp/javac.py 39 // Colors are based on clang's output 40 var ( 41 filelinePrefix = `^([-.\w/\\]+.java:[0-9]+: )` 42 warningRe = regexp.MustCompile(filelinePrefix + `?(warning:) .*$`) 43 errorRe = regexp.MustCompile(filelinePrefix + `(.*?:) .*$`) 44 markerRe = regexp.MustCompile(`()\s*(\^)\s*$`) 45 46 escape = "\x1b" 47 reset = escape + "[0m" 48 bold = escape + "[1m" 49 red = escape + "[31m" 50 green = escape + "[32m" 51 magenta = escape + "[35m" 52 ) 53 54 func main() { 55 exitCode, err := Main(os.Stdout, os.Args[0], os.Args[1:]) 56 if err != nil { 57 fmt.Fprintln(os.Stderr, err.Error()) 58 } 59 os.Exit(exitCode) 60 } 61 62 func Main(out io.Writer, name string, args []string) (int, error) { 63 if len(args) < 1 { 64 return 1, fmt.Errorf("usage: %s javac ...", name) 65 } 66 67 pr, pw, err := os.Pipe() 68 if err != nil { 69 return 1, fmt.Errorf("creating output pipe: %s", err) 70 } 71 72 cmd := exec.Command(args[0], args[1:]...) 73 cmd.Stdin = os.Stdin 74 cmd.Stdout = pw 75 cmd.Stderr = pw 76 err = cmd.Start() 77 if err != nil { 78 return 1, fmt.Errorf("starting subprocess: %s", err) 79 } 80 81 pw.Close() 82 83 // Process subprocess stdout asynchronously 84 errCh := make(chan error) 85 go func() { 86 errCh <- process(pr, out) 87 }() 88 89 // Wait for subprocess to finish 90 cmdErr := cmd.Wait() 91 92 // Wait for asynchronous stdout processing to finish 93 err = <-errCh 94 95 // Check for subprocess exit code 96 if cmdErr != nil { 97 if exitErr, ok := cmdErr.(*exec.ExitError); ok { 98 if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { 99 if status.Exited() { 100 return status.ExitStatus(), nil 101 } else if status.Signaled() { 102 exitCode := 128 + int(status.Signal()) 103 return exitCode, nil 104 } else { 105 return 1, exitErr 106 } 107 } else { 108 return 1, nil 109 } 110 } 111 } 112 113 if err != nil { 114 return 1, err 115 } 116 117 return 0, nil 118 } 119 120 func process(r io.Reader, w io.Writer) error { 121 scanner := bufio.NewScanner(r) 122 // Some javac wrappers output the entire list of java files being 123 // compiled on a single line, which can be very large, set the maximum 124 // buffer size to 2MB. 125 scanner.Buffer(nil, 2*1024*1024) 126 for scanner.Scan() { 127 processLine(w, scanner.Text()) 128 } 129 err := scanner.Err() 130 if err != nil { 131 return fmt.Errorf("scanning input: %s", err) 132 } 133 return nil 134 } 135 136 func processLine(w io.Writer, line string) { 137 for _, f := range filters { 138 if f.MatchString(line) { 139 return 140 } 141 } 142 for _, p := range colorPatterns { 143 var matched bool 144 if line, matched = applyColor(line, p.color, p.re); matched { 145 break 146 } 147 } 148 fmt.Fprintln(w, line) 149 } 150 151 // If line matches re, make it bold and apply color to the first submatch 152 // Returns line, modified if it matched, and true if it matched. 153 func applyColor(line, color string, re *regexp.Regexp) (string, bool) { 154 if m := re.FindStringSubmatchIndex(line); m != nil { 155 tagStart, tagEnd := m[4], m[5] 156 line = bold + line[:tagStart] + 157 color + line[tagStart:tagEnd] + reset + bold + 158 line[tagEnd:] + reset 159 return line, true 160 } 161 return line, false 162 } 163 164 var colorPatterns = []struct { 165 re *regexp.Regexp 166 color string 167 }{ 168 {warningRe, magenta}, 169 {errorRe, red}, 170 {markerRe, green}, 171 } 172 173 var filters = []*regexp.Regexp{ 174 regexp.MustCompile(`Note: (Some input files|.*\.java) uses? or overrides? a deprecated API.`), 175 regexp.MustCompile(`Note: Recompile with -Xlint:deprecation for details.`), 176 regexp.MustCompile(`Note: (Some input files|.*\.java) uses? unchecked or unsafe operations.`), 177 regexp.MustCompile(`Note: Recompile with -Xlint:unchecked for details.`), 178 regexp.MustCompile(`bootstrap class path not set in conjunction with -source`), 179 }