LangtoolsIdeaAntLogger.java revision 3294:9adfb22ff08f
1139823Simp/* 244165Sjulian * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 344165Sjulian * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 444165Sjulian * 544165Sjulian * This code is free software; you can redistribute it and/or modify it 644165Sjulian * under the terms of the GNU General Public License version 2 only, as 744165Sjulian * published by the Free Software Foundation. Oracle designates this 844165Sjulian * particular file as subject to the "Classpath" exception as provided 944165Sjulian * by Oracle in the LICENSE file that accompanied this code. 1044165Sjulian * 1144165Sjulian * This code is distributed in the hope that it will be useful, but WITHOUT 1244165Sjulian * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1344165Sjulian * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1444165Sjulian * version 2 for more details (a copy is included in the LICENSE file that 1544165Sjulian * accompanied this code). 1644165Sjulian * 1744165Sjulian * You should have received a copy of the GNU General Public License version 1844165Sjulian * 2 along with this work; if not, write to the Free Software Foundation, 1944165Sjulian * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2044165Sjulian * 2144165Sjulian * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2244165Sjulian * or visit www.oracle.com if you need additional information or have any 2344165Sjulian * questions. 2444165Sjulian */ 2544165Sjulian 2644165Sjulianpackage idea; 2744165Sjulian 2844165Sjulianimport org.apache.tools.ant.BuildEvent; 2944165Sjulianimport org.apache.tools.ant.BuildListener; 3044165Sjulianimport org.apache.tools.ant.DefaultLogger; 3144165Sjulianimport org.apache.tools.ant.Project; 3244165Sjulian 3350477Speterimport java.util.EnumSet; 3444165Sjulianimport java.util.Stack; 3544165Sjulian 3644165Sjulianimport static org.apache.tools.ant.Project.*; 3744165Sjulian 3844165Sjulian/** 3944165Sjulian * This class is used to wrap the IntelliJ ant logger in order to provide more meaningful 4044165Sjulian * output when building langtools. The basic ant output in IntelliJ can be quite cumbersome to 4144165Sjulian * work with, as it provides two separate views: (i) a tree view, which is good to display build task 4244165Sjulian * in a hierarchical fashion as they are processed; and a (ii) plain text view, which gives you 4344165Sjulian * the full ant output. The main problem is that javac-related messages are buried into the 4474408Smdodd * ant output (which is made very verbose by IntelliJ in order to support the tree view). It is 4544165Sjulian * not easy to figure out which node to expand in order to see the error message; switching 4644165Sjulian * to plain text doesn't help either, as now the output is totally flat. 4744165Sjulian * 48112271Smdodd * This logger class removes a lot of verbosity from the IntelliJ ant logger by not propagating 49112271Smdodd * all the events to the IntelliJ's logger. In addition, certain events are handled in a custom 5044165Sjulian * fashion, to generate better output during the build. 51112271Smdodd */ 5244165Sjulianpublic final class LangtoolsIdeaAntLogger extends DefaultLogger { 5344165Sjulian 5444165Sjulian /** 5544165Sjulian * This is just a way to pass in customized binary string predicates; 56257176Sglebius * 57184710Sbz * TODO: replace with @code{BiPredicate<String, String>} and method reference when moving to 8 58112271Smdodd */ 5944165Sjulian enum StringBinaryPredicate { 6044165Sjulian CONTAINS() { 61186119Sqingli @Override 6244165Sjulian boolean apply(String s1, String s2) { 63184710Sbz return s1.contains(s2); 64112271Smdodd } 65112271Smdodd }, 66112271Smdodd STARTS_WITH { 6744165Sjulian @Override 6844165Sjulian boolean apply(String s1, String s2) { 6974408Smdodd return s1.startsWith(s2); 7044165Sjulian } 7144165Sjulian }; 7244165Sjulian 7344165Sjulian abstract boolean apply(String s1, String s2); 7474408Smdodd } 7574408Smdodd 7674408Smdodd /** 7744165Sjulian * Various kinds of ant messages that we shall intercept 78163606Srwatson */ 79163606Srwatson enum MessageKind { 80126907Srwatson 81112277Smdodd /** a javac error */ 82112277Smdodd JAVAC_ERROR(StringBinaryPredicate.CONTAINS, MSG_ERR, "error:", "compiler.err"), 83112273Smdodd /** a javac warning */ 84112294Smdodd JAVAC_WARNING(StringBinaryPredicate.CONTAINS, MSG_WARN, "warning:", "compiler.warn"), 85112273Smdodd /** a javac note */ 86112276Smdodd JAVAC_NOTE(StringBinaryPredicate.CONTAINS, MSG_INFO, "note:", "compiler.note"), 8774408Smdodd /** continuation of some javac error message */ 88112297Smdodd JAVAC_NESTED_DIAG(StringBinaryPredicate.STARTS_WITH, MSG_INFO, " "), 89112297Smdodd /** a javac crash */ 90112297Smdodd JAVAC_CRASH(StringBinaryPredicate.STARTS_WITH, MSG_ERR, "An exception has occurred in the compiler"), 9144165Sjulian /** jtreg test success */ 92152296Sru JTREG_TEST_PASSED(StringBinaryPredicate.STARTS_WITH, MSG_INFO, "Passed: "), 9344165Sjulian /** jtreg test failure */ 94112296Smdodd JTREG_TEST_FAILED(StringBinaryPredicate.STARTS_WITH, MSG_ERR, "FAILED: "), 95111774Smdodd /** jtreg test error */ 9644165Sjulian JTREG_TEST_ERROR(StringBinaryPredicate.STARTS_WITH, MSG_ERR, "Error: "), 97112296Smdodd /** jtreg report */ 98112296Smdodd JTREG_TEST_REPORT(StringBinaryPredicate.STARTS_WITH, MSG_INFO, "Report written"); 9944165Sjulian 10058313Slile StringBinaryPredicate sbp; 10158313Slile int priority; 102112297Smdodd String[] keys; 103112297Smdodd 104112297Smdodd MessageKind(StringBinaryPredicate sbp, int priority, String... keys) { 105112297Smdodd this.sbp = sbp; 106112297Smdodd this.priority = priority; 107112297Smdodd this.keys = keys; 108112297Smdodd } 109112297Smdodd 11044165Sjulian /** 11158313Slile * Does a given message string matches this kind? 11244165Sjulian */ 11344165Sjulian boolean matches(String s) { 11444165Sjulian for (String key : keys) { 115152315Sru if (sbp.apply(s, key)) { 116152315Sru return true; 117112297Smdodd } 118112272Smdodd } 119112272Smdodd return false; 120112272Smdodd } 121152296Sru } 122112297Smdodd 123112297Smdodd /** 124112297Smdodd * This enum is used to represent the list of tasks we need to keep track of during logging. 125112297Smdodd */ 126112297Smdodd enum Task { 12744165Sjulian /** exec task - invoked during compilation */ 12844165Sjulian JAVAC("exec", MessageKind.JAVAC_ERROR, MessageKind.JAVAC_WARNING, MessageKind.JAVAC_NOTE, 12974408Smdodd MessageKind.JAVAC_NESTED_DIAG, MessageKind.JAVAC_CRASH), 13074408Smdodd /** jtreg task - invoked during test execution */ 13174408Smdodd JTREG("jtreg", MessageKind.JTREG_TEST_PASSED, MessageKind.JTREG_TEST_FAILED, MessageKind.JTREG_TEST_ERROR, MessageKind.JTREG_TEST_REPORT), 13274408Smdodd /** initial synthetic task when the logger is created */ 13374408Smdodd ROOT("") { 13474408Smdodd @Override 13574408Smdodd boolean matches(String s) { 13674408Smdodd return false; 137112274Smdodd } 13874408Smdodd }, 13974408Smdodd /** synthetic task catching any other tasks not in this list */ 140112274Smdodd ANY("") { 14174408Smdodd @Override 142112274Smdodd boolean matches(String s) { 143112274Smdodd return true; 14474408Smdodd } 14574408Smdodd }; 14644165Sjulian 147194581Srdivacky String taskName; 14844165Sjulian MessageKind[] msgs; 149112274Smdodd 150112274Smdodd Task(String taskName, MessageKind... msgs) { 151112274Smdodd this.taskName = taskName; 15244165Sjulian this.msgs = msgs; 153112274Smdodd } 154112274Smdodd 155112274Smdodd boolean matches(String s) { 156112274Smdodd return s.equals(taskName); 15744165Sjulian } 15844165Sjulian } 15944165Sjulian 16044165Sjulian /** 16144165Sjulian * This enum is used to represent the list of targets we need to keep track of during logging. 16244165Sjulian * A regular expression is used to match a given target name. 16344165Sjulian */ 16444165Sjulian enum Target { 16584931Sfjoe /** jtreg target - executed when launching tests */ 16644165Sjulian JTREG("jtreg") { 16774408Smdodd @Override 16844165Sjulian String getDisplayMessage(BuildEvent e) { 16944165Sjulian return "Running jtreg tests: " + e.getProject().getProperty("jtreg.tests"); 17044165Sjulian } 17144165Sjulian }, 17244165Sjulian /** build bootstrap tool target - executed when bootstrapping javac */ 17344165Sjulian BUILD_BOOTSTRAP_JAVAC("build-bootstrap-javac-classes") { 174120047Smdodd @Override 17544165Sjulian String getDisplayMessage(BuildEvent e) { 17644165Sjulian return "Building bootstrap javac..."; 17744165Sjulian } 178152315Sru }, 17944165Sjulian /** build classes target - executed when building classes of given tool */ 18044165Sjulian BUILD_ALL_CLASSES("build-all-classes") { 18144165Sjulian @Override 18244165Sjulian String getDisplayMessage(BuildEvent e) { 18344165Sjulian return "Building all classes..."; 18444165Sjulian } 18544165Sjulian }, 18644165Sjulian /** synthetic target catching any other target not in this list */ 18758313Slile ANY("") { 18844165Sjulian @Override 18944165Sjulian String getDisplayMessage(BuildEvent e) { 19044165Sjulian return "Executing Ant target(s): " + e.getProject().getProperty("ant.project.invoked-targets"); 19144165Sjulian } 19244165Sjulian @Override 193112274Smdodd boolean matches(String msg) { 194112274Smdodd return true; 195112274Smdodd } 19644165Sjulian }; 197112274Smdodd 19844165Sjulian String targetName; 19944165Sjulian 20044165Sjulian Target(String targetName) { 20144165Sjulian this.targetName = targetName; 20244165Sjulian } 20344165Sjulian 20444165Sjulian boolean matches(String msg) { 205249925Sglebius return msg.equals(targetName); 206249925Sglebius } 20744165Sjulian 20874408Smdodd abstract String getDisplayMessage(BuildEvent e); 20987914Sjlemon } 21087914Sjlemon 21174408Smdodd /** 21244627Sjulian * A custom build event used to represent status changes which should be notified inside 21374408Smdodd * Intellij 214193891Sbz */ 215275196Smelifaro static class StatusEvent extends BuildEvent { 21644165Sjulian 217293544Smelifaro /** the target to which the status update refers */ 218293544Smelifaro Target target; 219112285Smdodd 220172930Srwatson StatusEvent(BuildEvent e, Target target) { 221112285Smdodd super(new StatusTask(e, target.getDisplayMessage(e))); 222112285Smdodd this.target = target; 223112285Smdodd setMessage(getTask().getTaskName(), 2); 224112285Smdodd } 225112308Smdodd 226112308Smdodd /** 227148887Srwatson * A custom task used to channel info regarding a status change 228148887Srwatson */ 22944165Sjulian static class StatusTask extends org.apache.tools.ant.Task { 23074408Smdodd StatusTask(BuildEvent event, String msg) { 23174408Smdodd setProject(event.getProject()); 232128636Sluigi setOwningTarget(event.getTarget()); 233128636Sluigi setTaskName(msg); 23444627Sjulian } 235186119Sqingli } 236102291Sarchie } 23796184Skbyanc 23844627Sjulian /** wrapped ant logger (IntelliJ's own logger) */ 23944627Sjulian DefaultLogger logger; 24058313Slile 24158313Slile /** flag - is this the first target we encounter? */ 242152315Sru boolean firstTarget = true; 243112278Smdodd 24444627Sjulian /** flag - should subsequenet failures be suppressed ? */ 24558313Slile boolean suppressTaskFailures = false; 24644627Sjulian 24796184Skbyanc /** flag - have we ran into a javac crash ? */ 24874408Smdodd boolean crashFound = false; 24996184Skbyanc 25096184Skbyanc /** stack of status changes associated with pending targets */ 25144627Sjulian Stack<StatusEvent> statusEvents = new Stack<>(); 25244627Sjulian 25344627Sjulian /** stack of pending tasks */ 25444165Sjulian Stack<Task> tasks = new Stack<>(); 25544165Sjulian 25644165Sjulian public LangtoolsIdeaAntLogger(Project project) { 257301217Sgnn for (Object o : project.getBuildListeners()) { 258128636Sluigi if (o instanceof DefaultLogger) { 259128636Sluigi this.logger = (DefaultLogger)o; 26074408Smdodd project.removeBuildListener((BuildListener)o); 26174408Smdodd project.addBuildListener(this); 262126951Smdodd } 263126951Smdodd } 264126951Smdodd tasks.push(Task.ROOT); 265126951Smdodd } 266126951Smdodd 267126951Smdodd @Override 268126951Smdodd public void buildStarted(BuildEvent event) { 269126951Smdodd //do nothing 270126951Smdodd } 271126951Smdodd 272126951Smdodd @Override 273126951Smdodd public void buildFinished(BuildEvent event) { 274126951Smdodd //do nothing 275126951Smdodd } 276126951Smdodd 277126951Smdodd @Override 278126951Smdodd public void targetStarted(BuildEvent event) { 279126951Smdodd EnumSet<Target> statusKinds = firstTarget ? 280126951Smdodd EnumSet.allOf(Target.class) : 281126951Smdodd EnumSet.complementOf(EnumSet.of(Target.ANY)); 282126951Smdodd 283126951Smdodd String targetName = event.getTarget().getName(); 284126951Smdodd 285126951Smdodd for (Target statusKind : statusKinds) { 286126951Smdodd if (statusKind.matches(targetName)) { 287126951Smdodd StatusEvent statusEvent = new StatusEvent(event, statusKind); 288126951Smdodd statusEvents.push(statusEvent); 28974408Smdodd logger.taskStarted(statusEvent); 29074408Smdodd firstTarget = false; 29174408Smdodd return; 292301217Sgnn } 293128636Sluigi } 294287861Smelifaro } 29574408Smdodd 29674408Smdodd @Override 29774408Smdodd public void targetFinished(BuildEvent event) { 29844165Sjulian if (!statusEvents.isEmpty()) { 29974408Smdodd StatusEvent lastEvent = statusEvents.pop(); 300249925Sglebius if (lastEvent.target.matches(event.getTarget().getName())) { 30144627Sjulian logger.taskFinished(lastEvent); 30244627Sjulian } 30344627Sjulian } 30444627Sjulian } 30544627Sjulian 30644627Sjulian @Override 307108533Sschweikh public void taskStarted(BuildEvent event) { 30844627Sjulian String taskName = event.getTask().getTaskName(); 30944165Sjulian for (Task task : Task.values()) { 310249925Sglebius if (task.matches(taskName)) { 31144627Sjulian tasks.push(task); 31244627Sjulian return; 313249925Sglebius } 314249925Sglebius } 315249925Sglebius } 31644627Sjulian 31744165Sjulian @Override 31874408Smdodd public void taskFinished(BuildEvent event) { 31944165Sjulian if (tasks.peek() == Task.ROOT) { 320105598Sbrooks //we need to 'close' the root task to get nicer output 32144165Sjulian logger.taskFinished(event); 32274408Smdodd } else if (!suppressTaskFailures && event.getException() != null) { 32344165Sjulian //the first (innermost) task failure should always be logged 32444165Sjulian event.setMessage(event.getException().toString(), 0); 325112274Smdodd event.setException(null); 326112274Smdodd //note: we turn this into a plain message to avoid stack trace being logged by Idea 327112274Smdodd logger.messageLogged(event); 32874408Smdodd suppressTaskFailures = true; 32974408Smdodd } 330243882Sglebius tasks.pop(); 331298075Spfg } 33274408Smdodd 33374408Smdodd @Override 334112281Smdodd public void messageLogged(BuildEvent event) { 33574408Smdodd String msg = event.getMessage(); 336112268Smdodd 337112268Smdodd boolean processed = false; 338112268Smdodd 339112274Smdodd if (!tasks.isEmpty()) { 34074408Smdodd Task task = tasks.peek(); 34174408Smdodd for (MessageKind messageKind : task.msgs) { 34244165Sjulian if (messageKind.matches(msg)) { 34344165Sjulian event.setMessage(msg, messageKind.priority); 34444165Sjulian processed = true; 34544165Sjulian if (messageKind == MessageKind.JAVAC_CRASH) { 346243882Sglebius crashFound = true; 347298075Spfg } 34844165Sjulian break; 349112274Smdodd } 350112291Smdodd } 35144627Sjulian } 35244627Sjulian 35344627Sjulian if (event.getPriority() == MSG_ERR || crashFound) { 35444627Sjulian //we log errors regardless of owning task 35544165Sjulian logger.messageLogged(event); 35644165Sjulian suppressTaskFailures = true; 35744165Sjulian } else if (processed) { 35844165Sjulian logger.messageLogged(event); 35944165Sjulian } 36044165Sjulian } 36144165Sjulian} 36244165Sjulian