|
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,60 @@ 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 | + }; |
| 175 | + |
| 176 | + // javac.properties-> javac.msg.resource |
| 177 | + // (en JDK-8, ja JDK-8, zh_CN JDK-8, en JDK-21, ja JDK-21, zh_CN JDK-21, de JDK-21) |
| 178 | + protected static final String[] SYSTEM_OUT_OF_RESOURCES_ERROR_HEADERS = { |
| 179 | + "\n\nThe system is out of resources.\nConsult the following stack trace for details.\n", |
| 180 | + "\n\nシステム・リソースが不足しています。\n詳細は次のスタック・トレースで調査してください。\n", |
| 181 | + "\n\n系统资源不足。\n有关详细信息, 请参阅以下堆栈跟踪。\n", |
| 182 | + "\n\nThe system is out of resources.\nConsult the following stack trace for details.\n", |
| 183 | + "\n\nシステム・リソースが不足しています。\n詳細は次のスタックトレースで調査してください。\n", |
| 184 | + "\n\n系统资源不足。\n有关详细信息, 请参阅以下堆栈跟踪。\n", |
| 185 | + "\n\nDas System hat keine Ressourcen mehr.\nDetails finden Sie im folgenden Stacktrace.\n" |
| 186 | + }; |
132 | 187 | }
|
133 | 188 |
|
134 | 189 | private static final Object LOCK = new Object();
|
@@ -674,10 +729,6 @@ private static CompilerResult compileInProcess0(Class<?> javacClass, String[] ar
|
674 | 729 | private static final Pattern STACK_TRACE_OTHER_LINE =
|
675 | 730 | Pattern.compile("^(?:Caused by:\\s.*|\\s*at .*|\\s*\\.\\.\\.\\s\\d+\\smore)$");
|
676 | 731 |
|
677 |
| - // Match generic javac errors with 'javac:' prefix, JMV init and boot layer init errors |
678 |
| - private static final Pattern JAVAC_OR_JVM_ERROR = |
679 |
| - Pattern.compile("^(?:javac:|Error occurred during initialization of (?:boot layer|VM)).*", Pattern.DOTALL); |
680 |
| - |
681 | 732 | /**
|
682 | 733 | * Parse the compiler output into a list of compiler messages
|
683 | 734 | *
|
@@ -736,73 +787,136 @@ static List<CompilerMessage> parseModernStream(int exitCode, BufferedReader inpu
|
736 | 787 | }
|
737 | 788 | }
|
738 | 789 |
|
| 790 | + String bufferContent = buffer.toString(); |
| 791 | + if (bufferContent.isEmpty()) { |
| 792 | + return errors; |
| 793 | + } |
| 794 | + |
739 | 795 | // javac output not detected by other parsing
|
740 | 796 | // maybe better to ignore only the summary and mark the rest as error
|
741 |
| - String bufferAsString = buffer.toString(); |
742 |
| - if (!bufferAsString.isEmpty()) { |
743 |
| - if (JAVAC_OR_JVM_ERROR.matcher(bufferAsString).matches()) { |
744 |
| - errors.add(new CompilerMessage(bufferAsString, ERROR)); |
745 |
| - } else if (hasPointer) { |
746 |
| - // A compiler message remains in buffer at end of parse stream |
747 |
| - errors.add(parseModernError(exitCode, bufferAsString)); |
748 |
| - } else if (stackTraceLineCount > 0) { |
749 |
| - // Extract stack trace from end of buffer |
750 |
| - String[] lines = bufferAsString.split("\\R"); |
751 |
| - int linesTotal = lines.length; |
752 |
| - buffer = new StringBuilder(); |
753 |
| - int firstLine = linesTotal - stackTraceLineCount; |
754 |
| - |
755 |
| - // Salvage Javac localized message 'javac.msg.bug' ("An exception has occurred in the |
756 |
| - // compiler ... Please file a bug") |
757 |
| - if (firstLine > 0) { |
758 |
| - final String lineBeforeStackTrace = lines[firstLine - 1]; |
759 |
| - // One of those two URL substrings should always appear, without regard to JVM locale. |
760 |
| - // TODO: Update, if the URL changes, last checked for JDK 21. |
761 |
| - if (lineBeforeStackTrace.contains("java.sun.com/webapps/bugreport") |
762 |
| - || lineBeforeStackTrace.contains("bugreport.java.com")) { |
763 |
| - firstLine--; |
764 |
| - } |
765 |
| - } |
766 |
| - |
767 |
| - // Note: For message 'javac.msg.proc.annotation.uncaught.exception' ("An annotation processor |
768 |
| - // threw an uncaught exception"), there is no locale-independent substring, and the header is |
769 |
| - // also multi-line. It was discarded in the removed method 'parseAnnotationProcessorStream', |
770 |
| - // and we continue to do so. |
771 |
| - |
772 |
| - for (int i = firstLine; i < linesTotal; i++) { |
773 |
| - buffer.append(lines[i]).append(EOL); |
774 |
| - } |
775 |
| - errors.add(new CompilerMessage(buffer.toString(), ERROR)); |
| 797 | + String cleanedUpMessage; |
| 798 | + if ((cleanedUpMessage = getJavacGenericError(bufferContent)) != null |
| 799 | + || (cleanedUpMessage = getBootLayerInitError(bufferContent)) != null |
| 800 | + || (cleanedUpMessage = getVMInitError(bufferContent)) != null |
| 801 | + || (cleanedUpMessage = getFileABugError(bufferContent)) != null |
| 802 | + || (cleanedUpMessage = getAnnotationProcessingError(bufferContent)) != null |
| 803 | + || (cleanedUpMessage = getSystemOutOfResourcesError(bufferContent)) != null) { |
| 804 | + errors.add(new CompilerMessage(cleanedUpMessage, ERROR)); |
| 805 | + } else if (hasPointer) { |
| 806 | + // A compiler message remains in buffer at end of parse stream |
| 807 | + errors.add(parseModernError(exitCode, bufferContent)); |
| 808 | + } else if (stackTraceLineCount > 0) { |
| 809 | + // Extract stack trace from end of buffer |
| 810 | + String[] lines = bufferContent.split("\\R"); |
| 811 | + int linesTotal = lines.length; |
| 812 | + buffer = new StringBuilder(); |
| 813 | + int firstLine = linesTotal - stackTraceLineCount; |
| 814 | + for (int i = firstLine; i < linesTotal; i++) { |
| 815 | + buffer.append(lines[i]).append(EOL); |
776 | 816 | }
|
| 817 | + errors.add(new CompilerMessage(buffer.toString(), ERROR)); |
777 | 818 | }
|
| 819 | + // TODO: Add something like this? Check if it creates more value or more unnecessary log output in general. |
| 820 | + // else { |
| 821 | + // // Fall-back, if still no error or stack trace was recognised |
| 822 | + // errors.add(new CompilerMessage(bufferContent, exitCode == 0 ? OTHER : ERROR)); |
| 823 | + // } |
| 824 | + |
778 | 825 | return errors;
|
779 | 826 | }
|
780 | 827 |
|
781 |
| - private static boolean isMisc(String line) { |
782 |
| - return startsWithPrefix(line, MISC_PREFIXES); |
| 828 | + private static boolean isMisc(String message) { |
| 829 | + return startsWithPrefix(message, MISC_PREFIXES); |
| 830 | + } |
| 831 | + |
| 832 | + private static boolean isNote(String message) { |
| 833 | + return startsWithPrefix(message, NOTE_PREFIXES); |
| 834 | + } |
| 835 | + |
| 836 | + private static boolean isWarning(String message) { |
| 837 | + return startsWithPrefix(message, WARNING_PREFIXES); |
| 838 | + } |
| 839 | + |
| 840 | + private static boolean isError(String message) { |
| 841 | + return startsWithPrefix(message, ERROR_PREFIXES); |
| 842 | + } |
| 843 | + |
| 844 | + private static String getJavacGenericError(String message) { |
| 845 | + return getTextStartingWithPrefix(message, JAVAC_GENERIC_ERROR_PREFIXES); |
783 | 846 | }
|
784 | 847 |
|
785 |
| - private static boolean isNote(String line) { |
786 |
| - return startsWithPrefix(line, NOTE_PREFIXES); |
| 848 | + private static String getVMInitError(String message) { |
| 849 | + return getTextStartingWithPrefix(message, VM_INIT_ERROR_HEADERS); |
787 | 850 | }
|
788 | 851 |
|
789 |
| - private static boolean isWarning(String line) { |
790 |
| - return startsWithPrefix(line, WARNING_PREFIXES); |
| 852 | + private static String getBootLayerInitError(String message) { |
| 853 | + return getTextStartingWithPrefix(message, BOOT_LAYER_INIT_ERROR_HEADERS); |
791 | 854 | }
|
792 | 855 |
|
793 |
| - private static boolean isError(String line) { |
794 |
| - return startsWithPrefix(line, ERROR_PREFIXES); |
| 856 | + private static String getFileABugError(String message) { |
| 857 | + return getTextStartingWithPrefix(message, FILE_A_BUG_ERROR_HEADERS); |
795 | 858 | }
|
796 | 859 |
|
797 |
| - private static boolean startsWithPrefix(String line, String[] prefixes) { |
| 860 | + private static String getAnnotationProcessingError(String message) { |
| 861 | + return getTextStartingWithPrefix(message, ANNOTATION_PROCESSING_ERROR_HEADERS); |
| 862 | + } |
| 863 | + |
| 864 | + private static String getSystemOutOfResourcesError(String message) { |
| 865 | + return getTextStartingWithPrefix(message, SYSTEM_OUT_OF_RESOURCES_ERROR_HEADERS); |
| 866 | + } |
| 867 | + |
| 868 | + private static boolean startsWithPrefix(String text, String[] prefixes) { |
798 | 869 | for (String prefix : prefixes) {
|
799 |
| - if (line.startsWith(prefix)) { |
| 870 | + if (text.startsWith(prefix)) { |
800 | 871 | return true;
|
801 | 872 | }
|
802 | 873 | }
|
803 | 874 | return false;
|
804 | 875 | }
|
805 | 876 |
|
| 877 | + /** |
| 878 | + * Identify and return a known javac error message prefix and all subsequent text - usually a stack trace - from a |
| 879 | + * javac log output buffer. |
| 880 | + * |
| 881 | + * @param text log buffer to search for a javac error message stack trace |
| 882 | + * @param prefixes array of strings in Java properties format, e.g. {@code "some error with line feed\nand parameter |
| 883 | + * placeholders {0} and {1}"} in multiple locales (hence the array). For the search, the |
| 884 | + * placeholders may be represented by any text in the log buffer. |
| 885 | + * @return if found, the error message + all subsequent text, otherwise {@code null} |
| 886 | + */ |
| 887 | + static String getTextStartingWithPrefix(String text, String[] prefixes) { |
| 888 | + // Implementation note: The properties format with placeholders makes it easy to just copy & paste values from |
| 889 | + // the JDK compared to having to convert them to regular expressions with ".*" instead of "{0}" and quote |
| 890 | + // special regex characters. This makes the implementation of this method more complex and potentially a bit |
| 891 | + // slower, but hopefully is worth the effort for the convenience of future developers maintaining this class. |
| 892 | + |
| 893 | + // Normalise line feeds to the UNIX format found in JDK multi-line messages in properties files |
| 894 | + text = text.replaceAll("\\R", "\n"); |
| 895 | + |
| 896 | + // Search text for given error message prefixes/headers, until the first match is found |
| 897 | + for (String prefix : prefixes) { |
| 898 | + // Split properties message along placeholders like "{0}", "{1}" etc. |
| 899 | + String[] prefixParts = prefix.split("\\{\\d+\\}"); |
| 900 | + for (int i = 0; i < prefixParts.length; i++) { |
| 901 | + // Make sure to treat split sections as literal text in search regex by enclosing them in "\Q" and "\E". |
| 902 | + // See https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html, search for "Quotation". |
| 903 | + prefixParts[i] = "\\Q" + prefixParts[i] + "\\E"; |
| 904 | + } |
| 905 | + // Join message parts, replacing properties placeholders by ".*" regex ones |
| 906 | + prefix = String.join(".*?", prefixParts); |
| 907 | + // Find prefix + subsequent text in Pattern.DOTALL mode, represented in regex as "(?s)". |
| 908 | + // This matches across line break boundaries. |
| 909 | + Matcher matcher = Pattern.compile("(?s).*(" + prefix + ".*)").matcher(text); |
| 910 | + if (matcher.matches()) { |
| 911 | + // Match -> cut off text before header and replace UNIX line breaks by platform ones again |
| 912 | + return matcher.replaceFirst("$1").replaceAll("\n", EOL); |
| 913 | + } |
| 914 | + } |
| 915 | + |
| 916 | + // No match |
| 917 | + return null; |
| 918 | + } |
| 919 | + |
806 | 920 | /**
|
807 | 921 | * Construct a compiler message object from a compiler output line
|
808 | 922 | *
|
|
0 commit comments