1*333d2b36SAndroid Build Coastguard Worker// Copyright 2017 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 Worker// soong_javac_wrapper expects a javac command line and argments, executes 16*333d2b36SAndroid Build Coastguard Worker// it, and produces an ANSI colorized version of the output on stdout. 17*333d2b36SAndroid Build Coastguard Worker// 18*333d2b36SAndroid Build Coastguard Worker// It also hides the unhelpful and unhideable "warning there is a warning" 19*333d2b36SAndroid Build Coastguard Worker// messages. 20*333d2b36SAndroid Build Coastguard Worker// 21*333d2b36SAndroid Build Coastguard Worker// Each javac build statement has an order-only dependency on the 22*333d2b36SAndroid Build Coastguard Worker// soong_javac_wrapper tool, which means the javac command will not be rerun 23*333d2b36SAndroid Build Coastguard Worker// if soong_javac_wrapper changes. That means that soong_javac_wrapper must 24*333d2b36SAndroid Build Coastguard Worker// not do anything that will affect the results of the build. 25*333d2b36SAndroid Build Coastguard Workerpackage main 26*333d2b36SAndroid Build Coastguard Worker 27*333d2b36SAndroid Build Coastguard Workerimport ( 28*333d2b36SAndroid Build Coastguard Worker "bufio" 29*333d2b36SAndroid Build Coastguard Worker "fmt" 30*333d2b36SAndroid Build Coastguard Worker "io" 31*333d2b36SAndroid Build Coastguard Worker "os" 32*333d2b36SAndroid Build Coastguard Worker "os/exec" 33*333d2b36SAndroid Build Coastguard Worker "regexp" 34*333d2b36SAndroid Build Coastguard Worker "strconv" 35*333d2b36SAndroid Build Coastguard Worker "syscall" 36*333d2b36SAndroid Build Coastguard Worker) 37*333d2b36SAndroid Build Coastguard Worker 38*333d2b36SAndroid Build Coastguard Worker// Regular expressions are based on 39*333d2b36SAndroid Build Coastguard Worker// https://chromium.googlesource.com/chromium/src/+/master/build/android/gyp/javac.py 40*333d2b36SAndroid Build Coastguard Worker// Colors are based on clang's output 41*333d2b36SAndroid Build Coastguard Workervar ( 42*333d2b36SAndroid Build Coastguard Worker filelinePrefix = `^([-.\w/\\]+.java:[0-9]+: )` 43*333d2b36SAndroid Build Coastguard Worker warningRe = regexp.MustCompile(filelinePrefix + `?(warning:) .*$`) 44*333d2b36SAndroid Build Coastguard Worker errorRe = regexp.MustCompile(filelinePrefix + `(.*?:) .*$`) 45*333d2b36SAndroid Build Coastguard Worker markerRe = regexp.MustCompile(`()\s*(\^)\s*$`) 46*333d2b36SAndroid Build Coastguard Worker 47*333d2b36SAndroid Build Coastguard Worker escape = "\x1b" 48*333d2b36SAndroid Build Coastguard Worker reset = escape + "[0m" 49*333d2b36SAndroid Build Coastguard Worker bold = escape + "[1m" 50*333d2b36SAndroid Build Coastguard Worker red = escape + "[31m" 51*333d2b36SAndroid Build Coastguard Worker green = escape + "[32m" 52*333d2b36SAndroid Build Coastguard Worker magenta = escape + "[35m" 53*333d2b36SAndroid Build Coastguard Worker) 54*333d2b36SAndroid Build Coastguard Worker 55*333d2b36SAndroid Build Coastguard Workerfunc main() { 56*333d2b36SAndroid Build Coastguard Worker exitCode, err := Main(os.Stdout, os.Args[0], os.Args[1:]) 57*333d2b36SAndroid Build Coastguard Worker if err != nil { 58*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, err.Error()) 59*333d2b36SAndroid Build Coastguard Worker } 60*333d2b36SAndroid Build Coastguard Worker os.Exit(exitCode) 61*333d2b36SAndroid Build Coastguard Worker} 62*333d2b36SAndroid Build Coastguard Worker 63*333d2b36SAndroid Build Coastguard Workerfunc Main(out io.Writer, name string, args []string) (int, error) { 64*333d2b36SAndroid Build Coastguard Worker if len(args) < 1 { 65*333d2b36SAndroid Build Coastguard Worker return 1, fmt.Errorf("usage: %s javac ...", name) 66*333d2b36SAndroid Build Coastguard Worker } 67*333d2b36SAndroid Build Coastguard Worker 68*333d2b36SAndroid Build Coastguard Worker pr, pw, err := os.Pipe() 69*333d2b36SAndroid Build Coastguard Worker if err != nil { 70*333d2b36SAndroid Build Coastguard Worker return 1, fmt.Errorf("creating output pipe: %s", err) 71*333d2b36SAndroid Build Coastguard Worker } 72*333d2b36SAndroid Build Coastguard Worker 73*333d2b36SAndroid Build Coastguard Worker cmd := exec.Command(args[0], args[1:]...) 74*333d2b36SAndroid Build Coastguard Worker cmd.Stdin = os.Stdin 75*333d2b36SAndroid Build Coastguard Worker cmd.Stdout = pw 76*333d2b36SAndroid Build Coastguard Worker cmd.Stderr = pw 77*333d2b36SAndroid Build Coastguard Worker err = cmd.Start() 78*333d2b36SAndroid Build Coastguard Worker if err != nil { 79*333d2b36SAndroid Build Coastguard Worker return 1, fmt.Errorf("starting subprocess: %s", err) 80*333d2b36SAndroid Build Coastguard Worker } 81*333d2b36SAndroid Build Coastguard Worker 82*333d2b36SAndroid Build Coastguard Worker pw.Close() 83*333d2b36SAndroid Build Coastguard Worker 84*333d2b36SAndroid Build Coastguard Worker proc := processor{} 85*333d2b36SAndroid Build Coastguard Worker // Process subprocess stdout asynchronously 86*333d2b36SAndroid Build Coastguard Worker errCh := make(chan error) 87*333d2b36SAndroid Build Coastguard Worker go func() { 88*333d2b36SAndroid Build Coastguard Worker errCh <- proc.process(pr, out) 89*333d2b36SAndroid Build Coastguard Worker }() 90*333d2b36SAndroid Build Coastguard Worker 91*333d2b36SAndroid Build Coastguard Worker // Wait for subprocess to finish 92*333d2b36SAndroid Build Coastguard Worker cmdErr := cmd.Wait() 93*333d2b36SAndroid Build Coastguard Worker 94*333d2b36SAndroid Build Coastguard Worker // Wait for asynchronous stdout processing to finish 95*333d2b36SAndroid Build Coastguard Worker err = <-errCh 96*333d2b36SAndroid Build Coastguard Worker 97*333d2b36SAndroid Build Coastguard Worker // Check for subprocess exit code 98*333d2b36SAndroid Build Coastguard Worker if cmdErr != nil { 99*333d2b36SAndroid Build Coastguard Worker if exitErr, ok := cmdErr.(*exec.ExitError); ok { 100*333d2b36SAndroid Build Coastguard Worker if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { 101*333d2b36SAndroid Build Coastguard Worker if status.Exited() { 102*333d2b36SAndroid Build Coastguard Worker return status.ExitStatus(), nil 103*333d2b36SAndroid Build Coastguard Worker } else if status.Signaled() { 104*333d2b36SAndroid Build Coastguard Worker exitCode := 128 + int(status.Signal()) 105*333d2b36SAndroid Build Coastguard Worker return exitCode, nil 106*333d2b36SAndroid Build Coastguard Worker } else { 107*333d2b36SAndroid Build Coastguard Worker return 1, exitErr 108*333d2b36SAndroid Build Coastguard Worker } 109*333d2b36SAndroid Build Coastguard Worker } else { 110*333d2b36SAndroid Build Coastguard Worker return 1, nil 111*333d2b36SAndroid Build Coastguard Worker } 112*333d2b36SAndroid Build Coastguard Worker } 113*333d2b36SAndroid Build Coastguard Worker } 114*333d2b36SAndroid Build Coastguard Worker 115*333d2b36SAndroid Build Coastguard Worker if err != nil { 116*333d2b36SAndroid Build Coastguard Worker return 1, err 117*333d2b36SAndroid Build Coastguard Worker } 118*333d2b36SAndroid Build Coastguard Worker 119*333d2b36SAndroid Build Coastguard Worker return 0, nil 120*333d2b36SAndroid Build Coastguard Worker} 121*333d2b36SAndroid Build Coastguard Worker 122*333d2b36SAndroid Build Coastguard Workertype processor struct { 123*333d2b36SAndroid Build Coastguard Worker silencedWarnings int 124*333d2b36SAndroid Build Coastguard Worker} 125*333d2b36SAndroid Build Coastguard Worker 126*333d2b36SAndroid Build Coastguard Workerfunc (proc *processor) process(r io.Reader, w io.Writer) error { 127*333d2b36SAndroid Build Coastguard Worker scanner := bufio.NewScanner(r) 128*333d2b36SAndroid Build Coastguard Worker // Some javac wrappers output the entire list of java files being 129*333d2b36SAndroid Build Coastguard Worker // compiled on a single line, which can be very large, set the maximum 130*333d2b36SAndroid Build Coastguard Worker // buffer size to 2MB. 131*333d2b36SAndroid Build Coastguard Worker scanner.Buffer(nil, 2*1024*1024) 132*333d2b36SAndroid Build Coastguard Worker for scanner.Scan() { 133*333d2b36SAndroid Build Coastguard Worker proc.processLine(w, scanner.Text()) 134*333d2b36SAndroid Build Coastguard Worker } 135*333d2b36SAndroid Build Coastguard Worker err := scanner.Err() 136*333d2b36SAndroid Build Coastguard Worker if err != nil { 137*333d2b36SAndroid Build Coastguard Worker return fmt.Errorf("scanning input: %s", err) 138*333d2b36SAndroid Build Coastguard Worker } 139*333d2b36SAndroid Build Coastguard Worker return nil 140*333d2b36SAndroid Build Coastguard Worker} 141*333d2b36SAndroid Build Coastguard Worker 142*333d2b36SAndroid Build Coastguard Workerfunc (proc *processor) processLine(w io.Writer, line string) { 143*333d2b36SAndroid Build Coastguard Worker for _, f := range warningFilters { 144*333d2b36SAndroid Build Coastguard Worker if f.MatchString(line) { 145*333d2b36SAndroid Build Coastguard Worker proc.silencedWarnings++ 146*333d2b36SAndroid Build Coastguard Worker return 147*333d2b36SAndroid Build Coastguard Worker } 148*333d2b36SAndroid Build Coastguard Worker } 149*333d2b36SAndroid Build Coastguard Worker for _, f := range filters { 150*333d2b36SAndroid Build Coastguard Worker if f.MatchString(line) { 151*333d2b36SAndroid Build Coastguard Worker return 152*333d2b36SAndroid Build Coastguard Worker } 153*333d2b36SAndroid Build Coastguard Worker } 154*333d2b36SAndroid Build Coastguard Worker if match := warningCount.FindStringSubmatch(line); match != nil { 155*333d2b36SAndroid Build Coastguard Worker c, err := strconv.Atoi(match[1]) 156*333d2b36SAndroid Build Coastguard Worker if err == nil { 157*333d2b36SAndroid Build Coastguard Worker c -= proc.silencedWarnings 158*333d2b36SAndroid Build Coastguard Worker if c == 0 { 159*333d2b36SAndroid Build Coastguard Worker return 160*333d2b36SAndroid Build Coastguard Worker } else { 161*333d2b36SAndroid Build Coastguard Worker line = fmt.Sprintf("%d warning", c) 162*333d2b36SAndroid Build Coastguard Worker if c > 1 { 163*333d2b36SAndroid Build Coastguard Worker line += "s" 164*333d2b36SAndroid Build Coastguard Worker } 165*333d2b36SAndroid Build Coastguard Worker } 166*333d2b36SAndroid Build Coastguard Worker } 167*333d2b36SAndroid Build Coastguard Worker } 168*333d2b36SAndroid Build Coastguard Worker for _, p := range colorPatterns { 169*333d2b36SAndroid Build Coastguard Worker var matched bool 170*333d2b36SAndroid Build Coastguard Worker if line, matched = applyColor(line, p.color, p.re); matched { 171*333d2b36SAndroid Build Coastguard Worker break 172*333d2b36SAndroid Build Coastguard Worker } 173*333d2b36SAndroid Build Coastguard Worker } 174*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(w, line) 175*333d2b36SAndroid Build Coastguard Worker} 176*333d2b36SAndroid Build Coastguard Worker 177*333d2b36SAndroid Build Coastguard Worker// If line matches re, make it bold and apply color to the first submatch 178*333d2b36SAndroid Build Coastguard Worker// Returns line, modified if it matched, and true if it matched. 179*333d2b36SAndroid Build Coastguard Workerfunc applyColor(line, color string, re *regexp.Regexp) (string, bool) { 180*333d2b36SAndroid Build Coastguard Worker if m := re.FindStringSubmatchIndex(line); m != nil { 181*333d2b36SAndroid Build Coastguard Worker tagStart, tagEnd := m[4], m[5] 182*333d2b36SAndroid Build Coastguard Worker line = bold + line[:tagStart] + 183*333d2b36SAndroid Build Coastguard Worker color + line[tagStart:tagEnd] + reset + bold + 184*333d2b36SAndroid Build Coastguard Worker line[tagEnd:] + reset 185*333d2b36SAndroid Build Coastguard Worker return line, true 186*333d2b36SAndroid Build Coastguard Worker } 187*333d2b36SAndroid Build Coastguard Worker return line, false 188*333d2b36SAndroid Build Coastguard Worker} 189*333d2b36SAndroid Build Coastguard Worker 190*333d2b36SAndroid Build Coastguard Workervar colorPatterns = []struct { 191*333d2b36SAndroid Build Coastguard Worker re *regexp.Regexp 192*333d2b36SAndroid Build Coastguard Worker color string 193*333d2b36SAndroid Build Coastguard Worker}{ 194*333d2b36SAndroid Build Coastguard Worker {warningRe, magenta}, 195*333d2b36SAndroid Build Coastguard Worker {errorRe, red}, 196*333d2b36SAndroid Build Coastguard Worker {markerRe, green}, 197*333d2b36SAndroid Build Coastguard Worker} 198*333d2b36SAndroid Build Coastguard Worker 199*333d2b36SAndroid Build Coastguard Workervar warningCount = regexp.MustCompile(`^([0-9]+) warning(s)?$`) 200*333d2b36SAndroid Build Coastguard Worker 201*333d2b36SAndroid Build Coastguard Workervar warningFilters = []*regexp.Regexp{ 202*333d2b36SAndroid Build Coastguard Worker regexp.MustCompile(`bootstrap class path not set in conjunction with -source`), 203*333d2b36SAndroid Build Coastguard Worker} 204*333d2b36SAndroid Build Coastguard Worker 205*333d2b36SAndroid Build Coastguard Workervar filters = []*regexp.Regexp{ 206*333d2b36SAndroid Build Coastguard Worker regexp.MustCompile(`Note: (Some input files|.*\.java) uses? or overrides? a deprecated API.`), 207*333d2b36SAndroid Build Coastguard Worker regexp.MustCompile(`Note: Recompile with -Xlint:deprecation for details.`), 208*333d2b36SAndroid Build Coastguard Worker regexp.MustCompile(`Note: (Some input files|.*\.java) uses? unchecked or unsafe operations.`), 209*333d2b36SAndroid Build Coastguard Worker regexp.MustCompile(`Note: Recompile with -Xlint:unchecked for details.`), 210*333d2b36SAndroid Build Coastguard Worker 211*333d2b36SAndroid Build Coastguard Worker regexp.MustCompile(`javadoc: warning - The old Doclet and Taglet APIs in the packages`), 212*333d2b36SAndroid Build Coastguard Worker regexp.MustCompile(`com.sun.javadoc, com.sun.tools.doclets and their implementations`), 213*333d2b36SAndroid Build Coastguard Worker regexp.MustCompile(`are planned to be removed in a future JDK release. These`), 214*333d2b36SAndroid Build Coastguard Worker regexp.MustCompile(`components have been superseded by the new APIs in jdk.javadoc.doclet.`), 215*333d2b36SAndroid Build Coastguard Worker regexp.MustCompile(`Users are strongly recommended to migrate to the new APIs.`), 216*333d2b36SAndroid Build Coastguard Worker 217*333d2b36SAndroid Build Coastguard Worker regexp.MustCompile(`javadoc: option --boot-class-path not allowed with target 1.9`), 218*333d2b36SAndroid Build Coastguard Worker} 219