diff options
-rw-r--r-- | cmd/javac_filter/Android.bp | 23 | ||||
-rw-r--r-- | cmd/javac_filter/javac_filter.go | 105 | ||||
-rw-r--r-- | cmd/javac_filter/javac_filter_test.go | 74 |
3 files changed, 202 insertions, 0 deletions
diff --git a/cmd/javac_filter/Android.bp b/cmd/javac_filter/Android.bp new file mode 100644 index 00000000..cbdabb98 --- /dev/null +++ b/cmd/javac_filter/Android.bp @@ -0,0 +1,23 @@ +// 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. + +blueprint_go_binary { + name: "soong_javac_filter", + srcs: [ + "javac_filter.go", + ], + testSrcs: [ + "javac_filter_test.go", + ], +} diff --git a/cmd/javac_filter/javac_filter.go b/cmd/javac_filter/javac_filter.go new file mode 100644 index 00000000..32fcd639 --- /dev/null +++ b/cmd/javac_filter/javac_filter.go @@ -0,0 +1,105 @@ +// 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_filter expects the output of javac on stdin, and produces +// an ANSI colorized version of the output on stdout. +// +// It also hides the unhelpful and unhideable "warning there is a warning" +// messages. +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "regexp" +) + +// 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() { + err := process(bufio.NewReader(os.Stdin), os.Stdout) + if err != nil { + fmt.Fprintln(os.Stderr, "reading standard input:", err) + os.Exit(-1) + } +} + +func process(r io.Reader, w io.Writer) error { + scanner := bufio.NewScanner(r) + for scanner.Scan() { + processLine(w, scanner.Text()) + } + return scanner.Err() +} + +func processLine(w io.Writer, line string) { + for _, f := range filters { + if f.MatchString(line) { + return + } + } + 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[2], m[3] + 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 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.`), +} diff --git a/cmd/javac_filter/javac_filter_test.go b/cmd/javac_filter/javac_filter_test.go new file mode 100644 index 00000000..43381ce8 --- /dev/null +++ b/cmd/javac_filter/javac_filter_test.go @@ -0,0 +1,74 @@ +// 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. + +package main + +import ( + "bytes" + "testing" +) + +var testCases = []struct { + in, out string +}{ + { + in: "File.java:40: error: cannot find symbol\n", + out: "\x1b[1mFile.java:40: \x1b[31merror:\x1b[0m\x1b[1m cannot find symbol\x1b[0m\n", + }, + { + in: "import static com.blah.SYMBOL;\n", + out: "import static com.blah.SYMBOL;\n", + }, + { + in: " ^ \n", + out: "\x1b[1m \x1b[32m^\x1b[0m\x1b[1m \x1b[0m\n", + }, + { + in: "File.java:398: warning: [RectIntersectReturnValueIgnored] Return value of com.blah.function() must be checked\n", + out: "\x1b[1mFile.java:398: \x1b[35mwarning:\x1b[0m\x1b[1m [RectIntersectReturnValueIgnored] Return value of com.blah.function() must be checked\x1b[0m\n", + }, + { + in: " (see http://go/errorprone/bugpattern/RectIntersectReturnValueIgnored.md)\n", + out: " (see http://go/errorprone/bugpattern/RectIntersectReturnValueIgnored.md)\n", + }, + { + in: ` +Note: Some input files use or override a deprecated API. +Note: Recompile with -Xlint:deprecation for details. +Note: Some input files use unchecked or unsafe operations. +Note: Recompile with -Xlint:unchecked for details. +Note: dir/file.java uses or overrides a deprecated API. +Note: dir/file.java uses unchecked or unsafe operations. +`, + out: "\n", + }, + { + in: "\n", + out: "\n", + }, +} + +func TestJavacColorize(t *testing.T) { + for _, test := range testCases { + buf := new(bytes.Buffer) + err := process(bytes.NewReader([]byte(test.in)), buf) + if err != nil { + t.Errorf("error: %q", err) + } + got := string(buf.Bytes()) + if got != test.out { + t.Errorf("expected %q got %q", test.out, got) + } + } +} |