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  }