123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- // Copyright 2017 Google Inc. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- // soong_javac_wrapper expects a javac command line and argments, executes
- // it, and produces an ANSI colorized version of the output on stdout.
- //
- // It also hides the unhelpful and unhideable "warning there is a warning"
- // messages.
- //
- // Each javac build statement has an order-only dependency on the
- // soong_javac_wrapper tool, which means the javac command will not be rerun
- // if soong_javac_wrapper changes. That means that soong_javac_wrapper must
- // not do anything that will affect the results of the build.
- package main
- import (
- "bufio"
- "fmt"
- "io"
- "os"
- "os/exec"
- "regexp"
- "strconv"
- "syscall"
- )
- // Regular expressions are based on
- // https://chromium.googlesource.com/chromium/src/+/master/build/android/gyp/javac.py
- // Colors are based on clang's output
- var (
- filelinePrefix = `^([-.\w/\\]+.java:[0-9]+: )`
- warningRe = regexp.MustCompile(filelinePrefix + `?(warning:) .*$`)
- errorRe = regexp.MustCompile(filelinePrefix + `(.*?:) .*$`)
- markerRe = regexp.MustCompile(`()\s*(\^)\s*$`)
- escape = "\x1b"
- reset = escape + "[0m"
- bold = escape + "[1m"
- red = escape + "[31m"
- green = escape + "[32m"
- magenta = escape + "[35m"
- )
- func main() {
- exitCode, err := Main(os.Stdout, os.Args[0], os.Args[1:])
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- }
- os.Exit(exitCode)
- }
- func Main(out io.Writer, name string, args []string) (int, error) {
- if len(args) < 1 {
- return 1, fmt.Errorf("usage: %s javac ...", name)
- }
- pr, pw, err := os.Pipe()
- if err != nil {
- return 1, fmt.Errorf("creating output pipe: %s", err)
- }
- cmd := exec.Command(args[0], args[1:]...)
- cmd.Stdin = os.Stdin
- cmd.Stdout = pw
- cmd.Stderr = pw
- err = cmd.Start()
- if err != nil {
- return 1, fmt.Errorf("starting subprocess: %s", err)
- }
- pw.Close()
- proc := processor{}
- // Process subprocess stdout asynchronously
- errCh := make(chan error)
- go func() {
- errCh <- proc.process(pr, out)
- }()
- // Wait for subprocess to finish
- cmdErr := cmd.Wait()
- // Wait for asynchronous stdout processing to finish
- err = <-errCh
- // Check for subprocess exit code
- if cmdErr != nil {
- if exitErr, ok := cmdErr.(*exec.ExitError); ok {
- if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
- if status.Exited() {
- return status.ExitStatus(), nil
- } else if status.Signaled() {
- exitCode := 128 + int(status.Signal())
- return exitCode, nil
- } else {
- return 1, exitErr
- }
- } else {
- return 1, nil
- }
- }
- }
- if err != nil {
- return 1, err
- }
- return 0, nil
- }
- type processor struct {
- silencedWarnings int
- }
- func (proc *processor) process(r io.Reader, w io.Writer) error {
- scanner := bufio.NewScanner(r)
- // Some javac wrappers output the entire list of java files being
- // compiled on a single line, which can be very large, set the maximum
- // buffer size to 2MB.
- scanner.Buffer(nil, 2*1024*1024)
- for scanner.Scan() {
- proc.processLine(w, scanner.Text())
- }
- err := scanner.Err()
- if err != nil {
- return fmt.Errorf("scanning input: %s", err)
- }
- return nil
- }
- func (proc *processor) processLine(w io.Writer, line string) {
- for _, f := range warningFilters {
- if f.MatchString(line) {
- proc.silencedWarnings++
- return
- }
- }
- for _, f := range filters {
- if f.MatchString(line) {
- return
- }
- }
- if match := warningCount.FindStringSubmatch(line); match != nil {
- c, err := strconv.Atoi(match[1])
- if err == nil {
- c -= proc.silencedWarnings
- if c == 0 {
- return
- } else {
- line = fmt.Sprintf("%d warning", c)
- if c > 1 {
- line += "s"
- }
- }
- }
- }
- for _, p := range colorPatterns {
- var matched bool
- if line, matched = applyColor(line, p.color, p.re); matched {
- break
- }
- }
- fmt.Fprintln(w, line)
- }
- // If line matches re, make it bold and apply color to the first submatch
- // Returns line, modified if it matched, and true if it matched.
- func applyColor(line, color string, re *regexp.Regexp) (string, bool) {
- if m := re.FindStringSubmatchIndex(line); m != nil {
- tagStart, tagEnd := m[4], m[5]
- line = bold + line[:tagStart] +
- color + line[tagStart:tagEnd] + reset + bold +
- line[tagEnd:] + reset
- return line, true
- }
- return line, false
- }
- var colorPatterns = []struct {
- re *regexp.Regexp
- color string
- }{
- {warningRe, magenta},
- {errorRe, red},
- {markerRe, green},
- }
- var warningCount = regexp.MustCompile(`^([0-9]+) warning(s)?$`)
- var warningFilters = []*regexp.Regexp{
- regexp.MustCompile(`bootstrap class path not set in conjunction with -source`),
- }
- var filters = []*regexp.Regexp{
- regexp.MustCompile(`Note: (Some input files|.*\.java) uses? or overrides? a deprecated API.`),
- regexp.MustCompile(`Note: Recompile with -Xlint:deprecation for details.`),
- regexp.MustCompile(`Note: (Some input files|.*\.java) uses? unchecked or unsafe operations.`),
- regexp.MustCompile(`Note: Recompile with -Xlint:unchecked for details.`),
- regexp.MustCompile(`javadoc: warning - The old Doclet and Taglet APIs in the packages`),
- regexp.MustCompile(`com.sun.javadoc, com.sun.tools.doclets and their implementations`),
- regexp.MustCompile(`are planned to be removed in a future JDK release. These`),
- regexp.MustCompile(`components have been superseded by the new APIs in jdk.javadoc.doclet.`),
- regexp.MustCompile(`Users are strongly recommended to migrate to the new APIs.`),
- regexp.MustCompile(`javadoc: option --boot-class-path not allowed with target 1.9`),
- }
|