package com.intellij.jps.flex.build; import com.intellij.CommonBundle; import com.intellij.flex.FlexCommonBundle; import com.intellij.flex.FlexCommonUtils; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.PathUtilRt; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; import java.io.File; import java.util.regex.Matcher; public abstract class CompilerMessageHandlerBase { private static final String ERROR_PREFIX = "Error: "; private static Logger LOG = Logger.getInstance(CompilerMessageHandlerBase.class.getName()); private final CompileContext myContext; private final boolean myAsc20; private final String myCompilerName; private boolean myCompilationFinished; private boolean myCompilationFailed; private boolean myCompilationCancelled; private @Nullable String myPreviousUnreportedInfoMessage; /** * Implementations must call {@link #registerCompilationFinished()} at the end */ public CompilerMessageHandlerBase(final CompileContext context, final boolean asc20, final String compilerName) { myContext = context; myAsc20 = asc20; myCompilerName = compilerName; } public final void registerCompilationFinished() { myCompilationFinished = true; reportPreviousInfoMessage(); } public boolean isCompilationFailed() { LOG.assertTrue(myCompilationFinished, "compilationFinished() method not called yet"); return myCompilationFailed; } public boolean isCompilationCancelled() { LOG.assertTrue(myCompilationFinished, "compilationFinished() method not called yet"); return myCompilationCancelled; } protected abstract void onCancelled(); public void handleText(final String text) { for (String line : StringUtil.splitByLines(text)) { handleLine(line.trim()); } checkCancelled(); } private void handleLine(final String text) { if (StringUtil.isEmptyOrSpaces(text)) { reportPreviousInfoMessage(); return; } if (text.equals("^")) { myPreviousUnreportedInfoMessage = null; // don't report previous line, it contains a line of source code with warning/error return; } if ("command line".equals(text)) { // ignore this line and print previous if any reportPreviousInfoMessage(); return; } if (text.startsWith("Exception in thread \"") || text.contains(FlexCommonUtils.COULD_NOT_CREATE_JVM)) { reportPreviousInfoMessage(); myContext.processMessage(new CompilerMessage(myCompilerName, BuildMessage.Kind.ERROR, text)); myCompilationFailed = true; return; } if (myAsc20) { // see messages_en.properties from Falcon sources if (text.startsWith("Warning: ") || text.startsWith("Error: ") || text.startsWith("Syntax error: ") || text.startsWith("Internal error: ")) { final BuildMessage.Kind kind = text.startsWith("Warning: ") ? BuildMessage.Kind.WARNING : BuildMessage.Kind.ERROR; final int index = text.indexOf(": "); final String usefulMessage = text.substring(index + ": ".length()); final Pair<String, Integer> sourcePathAndLine = FlexCommonUtils.getSourcePathAndLineFromASC20Message(myPreviousUnreportedInfoMessage); if (sourcePathAndLine == null) { reportPreviousInfoMessage(); myContext.processMessage(new CompilerMessage(myCompilerName, kind, usefulMessage)); } else { myPreviousUnreportedInfoMessage = null; if (!isNotSupportedOptionFromGeneratedConfig(usefulMessage, sourcePathAndLine.first)) { myContext.processMessage( new CompilerMessage(myCompilerName, kind, usefulMessage, sourcePathAndLine.first, -1, -1, -1, sourcePathAndLine.second, 0)); } } myCompilationFailed |= kind == BuildMessage.Kind.ERROR; return; } } else { final Matcher matcher = FlexCommonUtils.ERROR_PATTERN.matcher(text); if (matcher.matches()) { final String sourceFilePath = matcher.group(1); final String additionalInfo = matcher.group(2); final String line = matcher.group(3); final String column = matcher.group(4); final String type = matcher.group(5); final String message = matcher.group(6); final BuildMessage.Kind kind = "Warning".equals(type) ? BuildMessage.Kind.WARNING : BuildMessage.Kind.ERROR; final File file = new File(sourceFilePath); final boolean sourceFileExists = file.exists(); final StringBuilder fullMessage = new StringBuilder(); if (!sourceFileExists) fullMessage.append(sourceFilePath).append(": "); if (additionalInfo != null) fullMessage.append(additionalInfo).append(' '); fullMessage.append(message); reportPreviousInfoMessage(); myContext.processMessage(new CompilerMessage(myCompilerName, kind, fullMessage.toString(), sourceFileExists ? sourceFilePath : null, -1, -1, -1, line != null ? Integer.parseInt(line) : 0, column != null ? Integer.parseInt(column) : 0)); myCompilationFailed |= kind == BuildMessage.Kind.ERROR; return; } } if (text.startsWith(ERROR_PREFIX)) { reportPreviousInfoMessage(); myContext.processMessage(new CompilerMessage(myCompilerName, BuildMessage.Kind.ERROR, text.substring(ERROR_PREFIX.length()))); myCompilationFailed = true; return; } reportPreviousInfoMessage(); myPreviousUnreportedInfoMessage = text; if (text.contains(FlexCommonUtils.OUT_OF_MEMORY) || text.contains(FlexCommonUtils.JAVA_HEAP_SPACE)) { myContext.processMessage( new CompilerMessage(myCompilerName, BuildMessage.Kind.ERROR, FlexCommonBundle.message("increase.flex.compiler.heap", CommonBundle.settingsActionPath()))); myCompilationFailed = true; } } private static boolean isNotSupportedOptionFromGeneratedConfig(final String message, final String filePath) { final String fileName = PathUtilRt.getFileName(filePath); return fileName.startsWith("idea-") && fileName.endsWith(".xml") && ("'compiler.locale' is not fully supported.".equals(message) || "'compiler.theme' is not fully supported.".equals(message) || "'compiler.preloader' is not fully supported.".equals(message) || "'compiler.accessible' is not fully supported.".equals(message) || "'compiler.fonts.managers' is not fully supported.".equals(message) || "'static-link-runtime-shared-libraries' is not fully supported.".equals(message)); } private void reportPreviousInfoMessage() { if (myPreviousUnreportedInfoMessage != null) { if (!myPreviousUnreportedInfoMessage.equals("<theme />") && !myPreviousUnreportedInfoMessage.equals("</locale>") && !myPreviousUnreportedInfoMessage.equals("<preloader>spark.preloaders.SplashScreen</preloader>") && !myPreviousUnreportedInfoMessage.equals("<accessible>true</accessible>") && !myPreviousUnreportedInfoMessage.equals("<accessible>false</accessible>") && !myPreviousUnreportedInfoMessage.equals("</managers>") && !myPreviousUnreportedInfoMessage.equals("<static-link-runtime-shared-libraries>false</static-link-runtime-shared-libraries>")) { myContext.processMessage(new CompilerMessage(myCompilerName, BuildMessage.Kind.INFO, myPreviousUnreportedInfoMessage)); } myPreviousUnreportedInfoMessage = null; } } private void checkCancelled() { if (!myCompilationCancelled && myContext.getCancelStatus().isCanceled()) { myCompilationCancelled = true; onCancelled(); } } }