|
66 | 66 | import java.util.Properties;
|
67 | 67 | import java.util.StringTokenizer;
|
68 | 68 | import java.util.concurrent.ConcurrentLinkedDeque;
|
| 69 | +import java.util.regex.Matcher; |
69 | 70 | import java.util.regex.Pattern;
|
70 | 71 |
|
71 | 72 | import org.codehaus.plexus.compiler.AbstractCompiler;
|
@@ -129,6 +130,48 @@ protected static class Messages {
|
129 | 130 |
|
130 | 131 | // compiler.properties -> compiler.misc.verbose.*
|
131 | 132 | protected static final String[] MISC_PREFIXES = {"["};
|
| 133 | + |
| 134 | + // Generic javac error prefix |
| 135 | + // TODO: In JDK 8, this generic prefix no longer seems to be in use for javac error messages, at least not in |
| 136 | + // the Java part of javac. Maybe in C sources? Does javac even use any native classes? |
| 137 | + protected static final String[] JAVAC_GENERIC_ERROR_PREFIXES = {"javac:"}; |
| 138 | + |
| 139 | + // Hard-coded, English-only error header in JVM native code, *not* followed by stack trace, but rather |
| 140 | + // by another text message |
| 141 | + protected static final String[] VM_INIT_ERROR_HEADERS = {"Error occurred during initialization of VM"}; |
| 142 | + |
| 143 | + // Hard-coded, English-only error header in class System, followed by stack trace |
| 144 | + protected static final String[] BOOT_LAYER_INIT_ERROR_HEADERS = { |
| 145 | + "Error occurred during initialization of boot layer" |
| 146 | + }; |
| 147 | + |
| 148 | + // javac.properties-> javac.msg.proc.annotation.uncaught.exception |
| 149 | + // (en JDK-8, ja JDK-8, zh_CN JDK-8, en JDK-21, ja JDK-21, zh_CN JDK-21, de JDK-21) |
| 150 | + protected static final String[] ANNOTATION_PROCESSING_ERROR_HEADERS = { |
| 151 | + "\n\nAn annotation processor threw an uncaught exception.\nConsult the following stack trace for details.\n\n", |
| 152 | + "\n\n注釈処理で捕捉されない例外がスローされました。\n詳細は次のスタック・トレースで調査してください。\n\n", |
| 153 | + "\n\n批注处理程序抛出未捕获的异常错误。\n有关详细信息, 请参阅以下堆栈跟踪。\n\n", |
| 154 | + "\n\nAn annotation processor threw an uncaught exception.\nConsult the following stack trace for details.\n\n", |
| 155 | + "\n\n注釈処理で捕捉されない例外がスローされました。\n詳細は次のスタックトレースで調査してください。\n\n", |
| 156 | + "\n\n批注处理程序抛出未捕获的异常错误。\n有关详细信息, 请参阅以下堆栈跟踪。\n\n", |
| 157 | + "\n\nEin Annotationsprozessor hat eine nicht abgefangene Ausnahme ausgelöst.\nDetails finden Sie im folgenden Stacktrace.\n\n" |
| 158 | + }; |
| 159 | + |
| 160 | + // javac.properties-> javac.msg.bug |
| 161 | + // (en JDK-8, ja JDK-8, zh_CN JDK-8, en JDK-9, ja JDK-9, zh_CN JDK-9, en JDK-21, ja JDK-21, zh_CN JDK-21, de |
| 162 | + // JDK-21) |
| 163 | + protected static final String[] FILE_A_BUG_ERROR_HEADERS = { |
| 164 | + "An exception has occurred in the compiler ({0}). Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport) after checking the Bug Parade for duplicates. Include your program and the following diagnostic in your report. Thank you.\n", |
| 165 | + "コンパイラで例外が発生しました({0})。Bug Paradeで重複がないかをご確認のうえ、Java Developer Connection (http://java.sun.com/webapps/bugreport)でbugの登録をお願いいたします。レポートには、そのプログラムと下記の診断内容を含めてください。ご協力ありがとうございます。\n", |
| 166 | + "编译器 ({0}) 中出现异常错误。 如果在 Bug Parade 中没有找到该错误, 请在 Java Developer Connection (http://java.sun.com/webapps/bugreport) 中建立 Bug。请在报告中附上您的程序和以下诊断信息。谢谢。\n", |
| 167 | + "An exception has occurred in the compiler ({0}). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program and the following diagnostic in your report. Thank you.", |
| 168 | + "コンパイラで例外が発生しました({0})。Bug Database (http://bugs.java.com)で重複がないかをご確認のうえ、Java bugレポート・ページ(http://bugreport.java.com)でJavaコンパイラに対するbugの登録をお願いいたします。レポートには、そのプログラムと下記の診断内容を含めてください。ご協力ありがとうございます。", |
| 169 | + "编译器 ({0}) 中出现异常错误。如果在 Bug Database (http://bugs.java.com) 中没有找到该错误, 请通过 Java Bug 报告页 (http://bugreport.java.com) 建立该 Java 编译器 Bug。请在报告中附上您的程序和以下诊断信息。谢谢。", |
| 170 | + "An exception has occurred in the compiler ({0}). Please file a bug against the Java compiler via the Java bug reporting page (https://bugreport.java.com) after checking the Bug Database (https://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.\n", |
| 171 | + "コンパイラで例外が発生しました({0})。バグ・データベース(https://bugs.java.com)で重複がないかをご確認のうえ、Javaのバグ・レポート・ページ(https://bugreport.java.com)から、Javaコンパイラに対するバグの登録をお願いいたします。レポートには、該当のプログラム、次の診断内容、およびJavaコンパイラに渡されたパラメータをご入力ください。ご協力ありがとうございます。\n", |
| 172 | + "编译器 ({0}) 中出现异常错误。如果在 Bug Database (https://bugs.java.com) 中没有找到有关该错误的 Java 编译器 Bug,请通过 Java Bug 报告页 (https://bugreport.java.com) 提交 Java 编译器 Bug。请在报告中附上您的程序、以下诊断信息以及传递到 Java 编译器的参数。谢谢。\n", |
| 173 | + "Im Compiler ({0}) ist eine Ausnahme aufgetreten. Erstellen Sie auf der Java-Seite zum Melden von Bugs (https://bugreport.java.com) einen Bugbericht, nachdem Sie die Bugdatenbank (https://bugs.java.com) auf Duplikate geprüft haben. Geben Sie in Ihrem Bericht Ihr Programm, die folgende Diagnose und die Parameter an, die Sie dem Java-Compiler übergeben haben. Vielen Dank.\n" |
| 174 | + }; |
132 | 175 | }
|
133 | 176 |
|
134 | 177 | private static final Object LOCK = new Object();
|
@@ -642,10 +685,6 @@ private static CompilerResult compileInProcess0(Class<?> javacClass, String[] ar
|
642 | 685 | private static final Pattern STACK_TRACE_OTHER_LINE =
|
643 | 686 | Pattern.compile("^(?:Caused by:\\s.*|\\s*at .*|\\s*\\.\\.\\.\\s\\d+\\smore)$");
|
644 | 687 |
|
645 |
| - // Match generic javac errors with 'javac:' prefix, JMV init and boot layer init errors |
646 |
| - private static final Pattern JAVAC_OR_JVM_ERROR = |
647 |
| - Pattern.compile("^(?:javac:|Error occurred during initialization of (?:boot layer|VM)).*", Pattern.DOTALL); |
648 |
| - |
649 | 688 | /**
|
650 | 689 | * Parse the compiler output into a list of compiler messages
|
651 | 690 | *
|
@@ -704,73 +743,131 @@ static List<CompilerMessage> parseModernStream(int exitCode, BufferedReader inpu
|
704 | 743 | }
|
705 | 744 | }
|
706 | 745 |
|
| 746 | + String bufferContent = buffer.toString(); |
| 747 | + if (bufferContent.isEmpty()) { |
| 748 | + return errors; |
| 749 | + } |
| 750 | + |
707 | 751 | // javac output not detected by other parsing
|
708 | 752 | // maybe better to ignore only the summary and mark the rest as error
|
709 |
| - String bufferAsString = buffer.toString(); |
710 |
| - if (!bufferAsString.isEmpty()) { |
711 |
| - if (JAVAC_OR_JVM_ERROR.matcher(bufferAsString).matches()) { |
712 |
| - errors.add(new CompilerMessage(bufferAsString, ERROR)); |
713 |
| - } else if (hasPointer) { |
714 |
| - // A compiler message remains in buffer at end of parse stream |
715 |
| - errors.add(parseModernError(exitCode, bufferAsString)); |
716 |
| - } else if (stackTraceLineCount > 0) { |
717 |
| - // Extract stack trace from end of buffer |
718 |
| - String[] lines = bufferAsString.split("\\R"); |
719 |
| - int linesTotal = lines.length; |
720 |
| - buffer = new StringBuilder(); |
721 |
| - int firstLine = linesTotal - stackTraceLineCount; |
722 |
| - |
723 |
| - // Salvage Javac localized message 'javac.msg.bug' ("An exception has occurred in the |
724 |
| - // compiler ... Please file a bug") |
725 |
| - if (firstLine > 0) { |
726 |
| - final String lineBeforeStackTrace = lines[firstLine - 1]; |
727 |
| - // One of those two URL substrings should always appear, without regard to JVM locale. |
728 |
| - // TODO: Update, if the URL changes, last checked for JDK 21. |
729 |
| - if (lineBeforeStackTrace.contains("java.sun.com/webapps/bugreport") |
730 |
| - || lineBeforeStackTrace.contains("bugreport.java.com")) { |
731 |
| - firstLine--; |
732 |
| - } |
733 |
| - } |
734 |
| - |
735 |
| - // Note: For message 'javac.msg.proc.annotation.uncaught.exception' ("An annotation processor |
736 |
| - // threw an uncaught exception"), there is no locale-independent substring, and the header is |
737 |
| - // also multi-line. It was discarded in the removed method 'parseAnnotationProcessorStream', |
738 |
| - // and we continue to do so. |
739 |
| - |
740 |
| - for (int i = firstLine; i < linesTotal; i++) { |
741 |
| - buffer.append(lines[i]).append(EOL); |
742 |
| - } |
743 |
| - errors.add(new CompilerMessage(buffer.toString(), ERROR)); |
| 753 | + String cleanedUpMessage; |
| 754 | + if ((cleanedUpMessage = getJavacGenericError(bufferContent)) != null |
| 755 | + || (cleanedUpMessage = getBootLayerInitError(bufferContent)) != null |
| 756 | + || (cleanedUpMessage = getVMInitError(bufferContent)) != null |
| 757 | + || (cleanedUpMessage = getFileABugError(bufferContent)) != null |
| 758 | + || (cleanedUpMessage = getAnnotationProcessingError(bufferContent)) != null) { |
| 759 | + errors.add(new CompilerMessage(cleanedUpMessage, ERROR)); |
| 760 | + } else if (hasPointer) { |
| 761 | + // A compiler message remains in buffer at end of parse stream |
| 762 | + errors.add(parseModernError(exitCode, bufferContent)); |
| 763 | + } else if (stackTraceLineCount > 0) { |
| 764 | + // Extract stack trace from end of buffer |
| 765 | + String[] lines = bufferContent.split("\\R"); |
| 766 | + int linesTotal = lines.length; |
| 767 | + buffer = new StringBuilder(); |
| 768 | + int firstLine = linesTotal - stackTraceLineCount; |
| 769 | + for (int i = firstLine; i < linesTotal; i++) { |
| 770 | + buffer.append(lines[i]).append(EOL); |
744 | 771 | }
|
| 772 | + errors.add(new CompilerMessage(buffer.toString(), ERROR)); |
745 | 773 | }
|
| 774 | + // TODO: Add something like this? Check if it creates more value or more unnecessary log output in general. |
| 775 | + // else { |
| 776 | + // // Fall-back, if still no error or stack trace was recognised |
| 777 | + // errors.add(new CompilerMessage(bufferContent, exitCode == 0 ? OTHER : ERROR)); |
| 778 | + // } |
| 779 | + |
746 | 780 | return errors;
|
747 | 781 | }
|
748 | 782 |
|
749 |
| - private static boolean isMisc(String line) { |
750 |
| - return startsWithPrefix(line, MISC_PREFIXES); |
| 783 | + private static boolean isMisc(String message) { |
| 784 | + return startsWithPrefix(message, MISC_PREFIXES); |
| 785 | + } |
| 786 | + |
| 787 | + private static boolean isNote(String message) { |
| 788 | + return startsWithPrefix(message, NOTE_PREFIXES); |
| 789 | + } |
| 790 | + |
| 791 | + private static boolean isWarning(String message) { |
| 792 | + return startsWithPrefix(message, WARNING_PREFIXES); |
| 793 | + } |
| 794 | + |
| 795 | + private static boolean isError(String message) { |
| 796 | + return startsWithPrefix(message, ERROR_PREFIXES); |
751 | 797 | }
|
752 | 798 |
|
753 |
| - private static boolean isNote(String line) { |
754 |
| - return startsWithPrefix(line, NOTE_PREFIXES); |
| 799 | + private static String getJavacGenericError(String message) { |
| 800 | + return getTextStartingWithPrefix(message, JAVAC_GENERIC_ERROR_PREFIXES); |
755 | 801 | }
|
756 | 802 |
|
757 |
| - private static boolean isWarning(String line) { |
758 |
| - return startsWithPrefix(line, WARNING_PREFIXES); |
| 803 | + private static String getVMInitError(String message) { |
| 804 | + return getTextStartingWithPrefix(message, VM_INIT_ERROR_HEADERS); |
759 | 805 | }
|
760 | 806 |
|
761 |
| - private static boolean isError(String line) { |
762 |
| - return startsWithPrefix(line, ERROR_PREFIXES); |
| 807 | + private static String getBootLayerInitError(String message) { |
| 808 | + return getTextStartingWithPrefix(message, BOOT_LAYER_INIT_ERROR_HEADERS); |
763 | 809 | }
|
764 | 810 |
|
765 |
| - private static boolean startsWithPrefix(String line, String[] prefixes) { |
| 811 | + private static String getFileABugError(String message) { |
| 812 | + return getTextStartingWithPrefix(message, FILE_A_BUG_ERROR_HEADERS); |
| 813 | + } |
| 814 | + |
| 815 | + private static String getAnnotationProcessingError(String message) { |
| 816 | + return getTextStartingWithPrefix(message, ANNOTATION_PROCESSING_ERROR_HEADERS); |
| 817 | + } |
| 818 | + |
| 819 | + private static boolean startsWithPrefix(String text, String[] prefixes) { |
766 | 820 | for (String prefix : prefixes) {
|
767 |
| - if (line.startsWith(prefix)) { |
| 821 | + if (text.startsWith(prefix)) { |
768 | 822 | return true;
|
769 | 823 | }
|
770 | 824 | }
|
771 | 825 | return false;
|
772 | 826 | }
|
773 | 827 |
|
| 828 | + /** |
| 829 | + * Identify and return a known javac error message prefix and all subsequent text - usually a stack trace - from a |
| 830 | + * javac log output buffer. |
| 831 | + * |
| 832 | + * @param text log buffer to search for a javac error message stack trace |
| 833 | + * @param prefixes array of strings in Java properties format, e.g. {@code "some error with line feed\nand parameter |
| 834 | + * placeholders {0} and {1}"} in multiple locales (hence the array). For the search, the |
| 835 | + * placeholders may be represented by any text in the log buffer. |
| 836 | + * @return if found, the error message + all subsequent text, otherwise {@code null} |
| 837 | + */ |
| 838 | + static String getTextStartingWithPrefix(String text, String[] prefixes) { |
| 839 | + // Implementation note: The properties format with placeholders makes it easy to just copy & paste values from |
| 840 | + // the JDK compared to having to convert them to regular expressions with ".*" instead of "{0}" and quote |
| 841 | + // special regex characters. This makes the implementation of this method more complex and potentially a bit |
| 842 | + // slower, but hopefully is worth the effort for the convenience of future developers maintaining this class. |
| 843 | + |
| 844 | + // Normalise line feeds to the UNIX format found in JDK multi-line messages in properties files |
| 845 | + text = text.replaceAll("\\R", "\n"); |
| 846 | + |
| 847 | + // Search text for given error message prefixes/headers, until the first match is found |
| 848 | + for (String prefix : prefixes) { |
| 849 | + // Split properties message along placeholders like "{0}", "{1}" etc. |
| 850 | + String[] prefixParts = prefix.split("\\{\\d+\\}"); |
| 851 | + for (int i = 0; i < prefixParts.length; i++) { |
| 852 | + // Make sure to treat split sections as literal text in search regex by enclosing them in "\Q" and "\E". |
| 853 | + // See https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html, search for "Quotation". |
| 854 | + prefixParts[i] = "\\Q" + prefixParts[i] + "\\E"; |
| 855 | + } |
| 856 | + // Join message parts, replacing properties placeholders by ".*" regex ones |
| 857 | + prefix = String.join(".*?", prefixParts); |
| 858 | + // Find prefix + subsequent text in Pattern.DOTALL mode, represented in regex as "(?s)". |
| 859 | + // This matches across line break boundaries. |
| 860 | + Matcher matcher = Pattern.compile("(?s).*(" + prefix + ".*)").matcher(text); |
| 861 | + if (matcher.matches()) { |
| 862 | + // Match -> cut off text before header and replace UNIX line breaks by platform ones again |
| 863 | + return matcher.replaceFirst("$1").replaceAll("\n", EOL); |
| 864 | + } |
| 865 | + } |
| 866 | + |
| 867 | + // No match |
| 868 | + return null; |
| 869 | + } |
| 870 | + |
774 | 871 | /**
|
775 | 872 | * Construct a compiler message object from a compiler output line
|
776 | 873 | *
|
|
0 commit comments