# HG changeset patch
# User mcimadamore
# Date 1406047665 -3600
# Node ID b71804e27eb17ab75e7b439aca1bae09c90fe72a
# Parent 86204c8c677179ddde4abbdfe0c4de002709b47b
8049234: Add support for running/debugging bootstrap tools in IntelliJ
Summary: Add support for bootstrap tools; overhaul ant logger
Reviewed-by: vromero
diff -r 86204c8c6771 -r b71804e27eb1 langtools/make/build.xml
--- a/langtools/make/build.xml Mon Jul 21 16:21:43 2014 -0700
+++ b/langtools/make/build.xml Tue Jul 22 17:47:45 2014 +0100
@@ -799,9 +799,15 @@
-
+
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
@@ -32,5 +29,15 @@
+
+
+
+
+
+
+
+
+
+
diff -r 86204c8c6771 -r b71804e27eb1 langtools/make/intellij/codeStyleSettings.xml
--- a/langtools/make/intellij/codeStyleSettings.xml Mon Jul 21 16:21:43 2014 -0700
+++ b/langtools/make/intellij/codeStyleSettings.xml Tue Jul 22 17:47:45 2014 +0100
@@ -5,10 +5,14 @@
-
+
+
+
+
+
diff -r 86204c8c6771 -r b71804e27eb1 langtools/make/intellij/compiler.xml
--- a/langtools/make/intellij/compiler.xml Mon Jul 21 16:21:43 2014 -0700
+++ b/langtools/make/intellij/compiler.xml Tue Jul 22 17:47:45 2014 +0100
@@ -3,28 +3,16 @@
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
diff -r 86204c8c6771 -r b71804e27eb1 langtools/make/intellij/copyright/langtools.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/make/intellij/copyright/langtools.xml Tue Jul 22 17:47:45 2014 +0100
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff -r 86204c8c6771 -r b71804e27eb1 langtools/make/intellij/copyright/profiles_settings.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/make/intellij/copyright/profiles_settings.xml Tue Jul 22 17:47:45 2014 +0100
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff -r 86204c8c6771 -r b71804e27eb1 langtools/make/intellij/inspectionProfiles/langtools.xml
--- a/langtools/make/intellij/inspectionProfiles/langtools.xml Mon Jul 21 16:21:43 2014 -0700
+++ b/langtools/make/intellij/inspectionProfiles/langtools.xml Tue Jul 22 17:47:45 2014 +0100
@@ -14,6 +14,10 @@
+
+
+
+
diff -r 86204c8c6771 -r b71804e27eb1 langtools/make/intellij/langtools.iml
--- a/langtools/make/intellij/langtools.iml Mon Jul 21 16:21:43 2014 -0700
+++ b/langtools/make/intellij/langtools.iml Tue Jul 22 17:47:45 2014 +0100
@@ -1,10 +1,13 @@
-
+
+
+
-
+
+
-
+
diff -r 86204c8c6771 -r b71804e27eb1 langtools/make/intellij/misc.xml
--- a/langtools/make/intellij/misc.xml Mon Jul 21 16:21:43 2014 -0700
+++ b/langtools/make/intellij/misc.xml Tue Jul 22 17:47:45 2014 +0100
@@ -4,7 +4,7 @@
-
+
diff -r 86204c8c6771 -r b71804e27eb1 langtools/make/intellij/src/idea/LangtoolsIdeaAntLogger.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/make/intellij/src/idea/LangtoolsIdeaAntLogger.java Tue Jul 22 17:47:45 2014 +0100
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package idea;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.DefaultLogger;
+
+import java.util.EnumSet;
+import java.util.Stack;
+
+import static org.apache.tools.ant.Project.*;
+
+/**
+ * This class is used to wrap the IntelliJ ant logger in order to provide more meaningful
+ * output when building langtools. The basic ant output in IntelliJ can be quite cumbersome to
+ * work with, as it provides two separate views: (i) a tree view, which is good to display build task
+ * in a hierarchical fashion as they are processed; and a (ii) plain text view, which gives you
+ * the full ant output. The main problem is that javac-related messages are buried into the
+ * ant output (which is made very verbose by IntelliJ in order to support the tree view). It is
+ * not easy to figure out which node to expand in order to see the error message; switching
+ * to plain text doesn't help either, as now the output is totally flat.
+ *
+ * This logger class removes a lot of verbosity from the IntelliJ ant logger by not propagating
+ * all the events to the IntelliJ's logger. In addition, certain events are handled in a custom
+ * fashion, to generate better output during the build.
+ */
+public final class LangtoolsIdeaAntLogger extends DefaultLogger {
+
+ /**
+ * This is just a way to pass in customized binary string predicates;
+ *
+ * TODO: replace with @code{BiPredicate} and method reference when moving to 8
+ */
+ enum StringBinaryPredicate {
+ CONTAINS() {
+ @Override
+ boolean apply(String s1, String s2) {
+ return s1.contains(s2);
+ }
+ },
+ STARTS_WITH {
+ @Override
+ boolean apply(String s1, String s2) {
+ return s1.startsWith(s2);
+ }
+ };
+
+ abstract boolean apply(String s1, String s2);
+ }
+
+ /**
+ * Various kinds of ant messages that we shall intercept
+ */
+ enum MessageKind {
+
+ /** a javac error */
+ JAVAC_ERROR(StringBinaryPredicate.CONTAINS, MSG_ERR, "error:", "compiler.err"),
+ /** a javac warning */
+ JAVAC_WARNING(StringBinaryPredicate.CONTAINS, MSG_WARN, "warning:", "compiler.warn"),
+ /** a javac note */
+ JAVAC_NOTE(StringBinaryPredicate.CONTAINS, MSG_INFO, "note:", "compiler.note"),
+ /** continuation of some javac error message */
+ JAVAC_NESTED_DIAG(StringBinaryPredicate.STARTS_WITH, MSG_INFO, " "),
+ /** a javac crash */
+ JAVAC_CRASH(StringBinaryPredicate.STARTS_WITH, MSG_ERR, "An exception has occurred in the compiler"),
+ /** jtreg test success */
+ JTREG_TEST_PASSED(StringBinaryPredicate.STARTS_WITH, MSG_INFO, "Passed: "),
+ /** jtreg test failure */
+ JTREG_TEST_FAILED(StringBinaryPredicate.STARTS_WITH, MSG_ERR, "FAILED: "),
+ /** jtreg test error */
+ JTREG_TEST_ERROR(StringBinaryPredicate.STARTS_WITH, MSG_ERR, "Error: ");
+
+ StringBinaryPredicate sbp;
+ int priority;
+ String[] keys;
+
+ MessageKind(StringBinaryPredicate sbp, int priority, String... keys) {
+ this.sbp = sbp;
+ this.priority = priority;
+ this.keys = keys;
+ }
+
+ /**
+ * Does a given message string matches this kind?
+ */
+ boolean matches(String s) {
+ for (String key : keys) {
+ if (sbp.apply(s, key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * This enum is used to represent the list of tasks we need to keep track of during logging.
+ */
+ enum Task {
+ /** javac task - invoked during compilation */
+ JAVAC("javac", MessageKind.JAVAC_ERROR, MessageKind.JAVAC_WARNING, MessageKind.JAVAC_NOTE,
+ MessageKind.JAVAC_NESTED_DIAG, MessageKind.JAVAC_CRASH),
+ /** jtreg task - invoked during test execution */
+ JTREG("jtreg", MessageKind.JTREG_TEST_PASSED, MessageKind.JTREG_TEST_FAILED, MessageKind.JTREG_TEST_ERROR),
+ /** initial synthetic task when the logger is created */
+ ROOT("") {
+ @Override
+ boolean matches(String s) {
+ return false;
+ }
+ },
+ /** synthetic task catching any other tasks not in this list */
+ ANY("") {
+ @Override
+ boolean matches(String s) {
+ return true;
+ }
+ };
+
+ String taskName;
+ MessageKind[] msgs;
+
+ Task(String taskName, MessageKind... msgs) {
+ this.taskName = taskName;
+ this.msgs = msgs;
+ }
+
+ boolean matches(String s) {
+ return s.equals(taskName);
+ }
+ }
+
+ /**
+ * This enum is used to represent the list of targets we need to keep track of during logging.
+ * A regular expression is used to match a given target name.
+ */
+ enum Target {
+ /** jtreg target - executed when launching tests */
+ JTREG("jtreg") {
+ @Override
+ String getDisplayMessage(BuildEvent e) {
+ return "Running jtreg tests: " + e.getProject().getProperty("jtreg.tests");
+ }
+ },
+ /** build bootstrap tool target - executed when bootstrapping javac */
+ BUILD_BOOTSTRAP_TOOL("build-bootstrap-.*") {
+ @Override
+ String getDisplayMessage(BuildEvent e) {
+ String targetName = e.getTarget().getName();
+ String tool = targetName.split("-")[2];
+ return "Building bootstrap " + tool + "...";
+ }
+ },
+ /** build classes target - executed when building classes of given tool */
+ BUILD_TOOL("build-classes-.*") {
+ @Override
+ String getDisplayMessage(BuildEvent e) {
+ String targetName = e.getTarget().getName();
+ String tool = targetName.split("-")[2];
+ return "Building " + tool + "...";
+ }
+ },
+ /** synthetic target catching any other target not in this list */
+ ANY("") {
+ @Override
+ String getDisplayMessage(BuildEvent e) {
+ return "Executing Ant target(s): " + e.getProject().getProperty("ant.project.invoked-targets");
+ }
+ @Override
+ boolean matches(String msg) {
+ return true;
+ }
+ };
+
+ String targetRegex;
+
+ Target(String targetRegex) {
+ this.targetRegex = targetRegex;
+ }
+
+ boolean matches(String msg) {
+ return msg.matches(targetRegex);
+ }
+
+ abstract String getDisplayMessage(BuildEvent e);
+ }
+
+ /**
+ * A custom build event used to represent status changes which should be notified inside
+ * Intellij
+ */
+ static class StatusEvent extends BuildEvent {
+
+ /** the target to which the status update refers */
+ Target target;
+
+ StatusEvent(BuildEvent e, Target target) {
+ super(new StatusTask(e, target.getDisplayMessage(e)));
+ this.target = target;
+ setMessage(getTask().getTaskName(), 2);
+ }
+
+ /**
+ * A custom task used to channel info regarding a status change
+ */
+ static class StatusTask extends org.apache.tools.ant.Task {
+ StatusTask(BuildEvent event, String msg) {
+ setProject(event.getProject());
+ setOwningTarget(event.getTarget());
+ setTaskName(msg);
+ }
+ }
+ }
+
+ /** wrapped ant logger (IntelliJ's own logger) */
+ DefaultLogger logger;
+
+ /** flag - is this the first target we encounter? */
+ boolean firstTarget = true;
+
+ /** flag - should subsequenet failures be suppressed ? */
+ boolean suppressTaskFailures = false;
+
+ /** flag - have we ran into a javac crash ? */
+ boolean crashFound = false;
+
+ /** stack of status changes associated with pending targets */
+ Stack statusEvents = new Stack<>();
+
+ /** stack of pending tasks */
+ Stack tasks = new Stack<>();
+
+ public LangtoolsIdeaAntLogger(DefaultLogger logger) {
+ this.logger = logger;
+ tasks.push(Task.ROOT);
+ }
+
+ @Override
+ public void buildStarted(BuildEvent event) {
+ //do nothing
+ }
+
+ @Override
+ public void buildFinished(BuildEvent event) {
+ //do nothing
+ }
+
+ @Override
+ public void targetStarted(BuildEvent event) {
+ EnumSet statusKinds = firstTarget ?
+ EnumSet.allOf(Target.class) :
+ EnumSet.complementOf(EnumSet.of(Target.ANY));
+
+ String targetName = event.getTarget().getName();
+
+ for (Target statusKind : statusKinds) {
+ if (statusKind.matches(targetName)) {
+ StatusEvent statusEvent = new StatusEvent(event, statusKind);
+ statusEvents.push(statusEvent);
+ logger.taskStarted(statusEvent);
+ firstTarget = false;
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void targetFinished(BuildEvent event) {
+ if (!statusEvents.isEmpty()) {
+ StatusEvent lastEvent = statusEvents.pop();
+ if (lastEvent.target.matches(event.getTarget().getName())) {
+ logger.taskFinished(lastEvent);
+ }
+ }
+ }
+
+ @Override
+ public void taskStarted(BuildEvent event) {
+ String taskName = event.getTask().getTaskName();
+ for (Task task : Task.values()) {
+ if (task.matches(taskName)) {
+ tasks.push(task);
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void taskFinished(BuildEvent event) {
+ if (tasks.peek() == Task.ROOT) {
+ //we need to 'close' the root task to get nicer output
+ logger.taskFinished(event);
+ } else if (!suppressTaskFailures && event.getException() != null) {
+ //the first (innermost) task failure should always be logged
+ event.setMessage(event.getException().toString(), 0);
+ event.setException(null);
+ //note: we turn this into a plain message to avoid stack trace being logged by Idea
+ logger.messageLogged(event);
+ suppressTaskFailures = true;
+ }
+ tasks.pop();
+ }
+
+ @Override
+ public void messageLogged(BuildEvent event) {
+ String msg = event.getMessage();
+
+ boolean processed = false;
+
+ if (!tasks.isEmpty()) {
+ Task task = tasks.peek();
+ for (MessageKind messageKind : task.msgs) {
+ if (messageKind.matches(msg)) {
+ event.setMessage(msg, messageKind.priority);
+ processed = true;
+ if (messageKind == MessageKind.JAVAC_CRASH) {
+ crashFound = true;
+ }
+ break;
+ }
+ }
+ }
+
+ if (event.getPriority() == MSG_ERR || crashFound) {
+ //we log errors regardless of owning task
+ logger.messageLogged(event);
+ suppressTaskFailures = true;
+ } else if (processed) {
+ logger.messageLogged(event);
+ }
+ }
+}
diff -r 86204c8c6771 -r b71804e27eb1 langtools/make/intellij/workspace.xml
--- a/langtools/make/intellij/workspace.xml Mon Jul 21 16:21:43 2014 -0700
+++ b/langtools/make/intellij/workspace.xml Tue Jul 22 17:47:45 2014 +0100
@@ -7,6 +7,7 @@
+
@@ -19,7 +20,12 @@
-
+
+
+
+
+
+
@@ -33,7 +39,12 @@
-
+
+
+
+
+
+
@@ -47,7 +58,12 @@
-
+
+
+
+
+
+
@@ -61,21 +77,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
@@ -89,9 +96,35 @@
-
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -108,15 +141,6 @@
-
-
-
-
-
-
-
-
-
@@ -131,8 +155,9 @@
-
+
+