8074716: IntelliJ IDEA project support
Reviewed-by: rriggs
Contributed-by: Chris Hegarty <chris.hegarty@oracle.com>, Maurizio Cimadamore <maurizio.cimadamore@oracle.com>
--- a/.hgignore Wed Jul 05 21:41:01 2017 +0200
+++ b/.hgignore Tue May 10 13:34:30 2016 +0100
@@ -1,5 +1,6 @@
^build/
^dist/
+^.idea/
nbproject/private/
^webrev
^.hgtip
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/common/bin/idea.sh Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,199 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, 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.
+#
+# 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.
+#
+
+# Shell script for generating an IDEA project from a given list of modules
+
+usage() {
+ echo "usage: $0 [-h|--help] [-v|--verbose] [-o|--output <path>] [modules]+"
+ exit 1
+}
+
+SCRIPT_DIR=`dirname $0`
+PWD=`pwd`
+cd $SCRIPT_DIR; SCRIPT_DIR=`pwd`
+cd ../../; TOP=`pwd`; cd $PWD
+
+IDEA_OUTPUT=$TOP/.idea
+VERBOSE="false"
+while [ $# -gt 0 ]
+do
+ case $1 in
+ -h | --help )
+ usage
+ ;;
+
+ -v | --vebose )
+ VERBOSE="true"
+ ;;
+
+ -o | --output )
+ IDEA_OUTPUT=$2
+ shift
+ ;;
+
+ -*) # bad option
+ usage
+ ;;
+
+ * ) # non option
+ break
+ ;;
+ esac
+ shift
+done
+
+mkdir $IDEA_OUTPUT || exit 1
+cd $IDEA_OUTPUT; IDEA_OUTPUT=`pwd`
+
+IDEA_MAKE="$TOP/make/idea"
+IDEA_TEMPLATE="$IDEA_MAKE/template"
+IML_TEMPLATE="$IDEA_TEMPLATE/jdk.iml"
+ANT_TEMPLATE="$IDEA_TEMPLATE/ant.xml"
+IDEA_IML="$IDEA_OUTPUT/jdk.iml"
+IDEA_ANT="$IDEA_OUTPUT/ant.xml"
+
+if [ "$VERBOSE" = "true" ] ; then
+ echo "output dir: $IDEA_OUTPUT"
+ echo "idea template dir: $IDEA_TEMPLATE"
+fi
+
+if [ ! -f "$IML_TEMPLATE" ] ; then
+ echo "FATAL: cannot find $IML_TEMPLATE" >&2; exit 1
+fi
+
+if [ ! -f "$ANT_TEMPLATE" ] ; then
+ echo "FATAL: cannot find $ANT_TEMPLATE" >&2; exit 1
+fi
+
+cp -r "$IDEA_TEMPLATE"/* "$IDEA_OUTPUT"
+cd $TOP ; make -f "$IDEA_MAKE/idea.gmk" -I make/common idea MAKEOVERRIDES= OUT=$IDEA_OUTPUT/env.cfg MODULES="$*" || exit 1
+cd $SCRIPT_DIR
+
+. $IDEA_OUTPUT/env.cfg
+
+# Expect MODULE_ROOTS, MODULE_NAMES, BOOT_JDK & SPEC to be set
+if [ "x$MODULE_ROOTS" = "x" ] ; then
+ echo "FATAL: MODULE_ROOTS is empty" >&2; exit 1
+fi
+
+if [ "x$MODULE_NAMES" = "x" ] ; then
+ echo "FATAL: MODULE_NAMES is empty" >&2; exit 1
+fi
+
+if [ "x$BOOT_JDK" = "x" ] ; then
+ echo "FATAL: BOOT_JDK is empty" >&2; exit 1
+fi
+
+if [ "x$SPEC" = "x" ] ; then
+ echo "FATAL: SPEC is empty" >&2; exit 1
+fi
+
+SOURCE_FOLDER=" <sourceFolder url=\"file://\$MODULE_DIR\$/####\" isTestSource=\"false\" />"
+SOURCE_FOLDERS_DONE="false"
+
+addSourceFolder() {
+ root=$@
+ relativePath="`echo "$root" | sed -e s@"$TOP/\(.*$\)"@"\1"@`"
+ folder="`echo "$SOURCE_FOLDER" | sed -e s@"\(.*/\)####\(.*\)"@"\1$relativePath\2"@`"
+ printf "%s\n" "$folder" >> $IDEA_IML
+}
+
+### Generate project iml
+RELATIVE_BUILD_DIR="`dirname $SPEC | sed -e s@"$TOP/\(.*$\)"@"\1"@`"
+rm -f $IDEA_IML
+while IFS= read -r line
+do
+ if echo "$line" | egrep "^ .* <sourceFolder.*####" > /dev/null ; then
+ if [ "$SOURCE_FOLDERS_DONE" = "false" ] ; then
+ SOURCE_FOLDERS_DONE="true"
+ for root in $MODULE_ROOTS; do
+ addSourceFolder $root
+ done
+ fi
+ elif echo "$line" | egrep "^ .* <excludeFolder.*####" > /dev/null ; then
+ ul="`echo "$line" | sed -e s@"\(.*/\)####\(.*\)"@"\1$RELATIVE_BUILD_DIR\2"@`"
+ printf "%s\n" "$ul" >> $IDEA_IML
+ else
+ printf "%s\n" "$line" >> $IDEA_IML
+ fi
+done < "$IML_TEMPLATE"
+
+
+MODULE_NAME=" <property name=\"module.name\" value=\"####\" />"
+
+addModuleName() {
+ mn="`echo "$MODULE_NAME" | sed -e s@"\(.*\)####\(.*\)"@"\1$MODULE_NAMES\2"@`"
+ printf "%s\n" "$mn" >> $IDEA_ANT
+}
+
+BUILD_DIR=" <property name=\"build.target.dir\" value=\"####\" />"
+
+addBuildDir() {
+ DIR=`dirname $SPEC`
+ mn="`echo "$BUILD_DIR" | sed -e s@"\(.*\)####\(.*\)"@"\1$DIR\2"@`"
+ printf "%s\n" "$mn" >> $IDEA_ANT
+}
+
+### Generate ant.xml
+
+rm -f $IDEA_ANT
+while IFS= read -r line
+do
+ if echo "$line" | egrep "^ .* <property name=\"module.name\"" > /dev/null ; then
+ addModuleName
+ elif echo "$line" | egrep "^ .* <property name=\"build.target.dir\"" > /dev/null ; then
+ addBuildDir
+ else
+ printf "%s\n" "$line" >> $IDEA_ANT
+ fi
+done < "$ANT_TEMPLATE"
+
+### Compile the custom Logger
+
+CLASSES=$IDEA_OUTPUT/classes
+
+if [ "x$ANT_HOME" = "x" ] ; then
+ # try some common locations, before giving up
+ if [ -f "/usr/share/ant/lib/ant.jar" ] ; then
+ ANT_HOME="/usr/share/ant"
+ elif [ -f "/usr/local/Cellar/ant/1.9.4/libexec/lib/ant.jar" ] ; then
+ ANT_HOME="/usr/local/Cellar/ant/1.9.4/libexec"
+ else
+ echo "FATAL: cannot find ant. Try setting ANT_HOME." >&2; exit 1
+ fi
+fi
+CP=$ANT_HOME/lib/ant.jar
+rm -rf $CLASSES; mkdir $CLASSES
+
+if [ "x$CYGPATH" = "x" ] ; then ## CYGPATH may be set in env.cfg
+ JAVAC_SOURCE_FILE=$IDEA_OUTPUT/src/idea/JdkIdeaAntLogger.java
+ JAVAC_CLASSES=$CLASSES
+ JAVAC_CP=$CP
+else
+ JAVAC_SOURCE_FILE=`cygpath -am $IDEA_OUTPUT/src/idea/JdkIdeaAntLogger.java`
+ JAVAC_CLASSES=`cygpath -am $CLASSES`
+ JAVAC_CP=`cygpath -am $CP`
+fi
+
+$BOOT_JDK/bin/javac -d $JAVAC_CLASSES -cp $JAVAC_CP $JAVAC_SOURCE_FILE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/idea.gmk Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,39 @@
+include Makefile
+include make/MainSupport.gmk
+
+.PHONY: idea
+
+ifeq ($(SPEC),)
+ ifneq ($(words $(SPECS)),1)
+ @echo "Error: Multiple build specification files found. Please select one explicitly."
+ @exit 2
+ endif
+ idea:
+ @cd $(topdir)
+ @$(MAKE) $(MFLAGS) $(MAKE_LOG_FLAGS) -r -R -j 1 -f $(topdir)/make/idea/idea.gmk SPEC=$(SPECS) HAS_SPEC=true ACTUAL_TOPDIR=$(topdir) MODULES="$(MODULES)" idea
+else #with SPEC
+ include make/common/Modules.gmk
+
+ ifeq ($(MODULES),)
+ SEL_MODULES := $(call FindAllModules)
+ else
+ SEL_MODULES := $(MODULES)
+ endif
+
+ # Find all source dirs for a particular module
+ # $1 - Module to find source dirs for
+ FindIdeaModuleSrcDirs = \
+ $(strip $(addsuffix /$(strip $1), $(GENERATED_SRC_DIRS) $(IMPORT_MODULES_SRC)) \
+ $(wildcard $(foreach sub, $(SRC_SUBDIRS), $(addsuffix /$(strip $1)/$(sub), $(TOP_SRC_DIRS)))))
+
+
+ idea:
+ $(ECHO) "SUPPORT=$(SUPPORT_OUTPUTDIR)" >> $(OUT)
+ $(ECHO) "MODULE_ROOTS=\"$(foreach mod, $(SEL_MODULES), $(call FindIdeaModuleSrcDirs,$(mod)))\"" >> $(OUT)
+ $(ECHO) "MODULE_NAMES=\"$(strip $(foreach mod, $(SEL_MODULES), $(mod)))\"" >> $(OUT)
+ $(ECHO) "SEL_MODULES=\"$(SEL_MODULES)\"" >> $(OUT)
+ $(ECHO) "BOOT_JDK=\"$(BOOT_JDK)\"" >> $(OUT)
+ $(ECHO) "CYGPATH=\"$(CYGPATH)\"" >> $(OUT)
+ $(ECHO) "SPEC=\"$(SPEC)\"" >> $(OUT)
+
+endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/.name Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,1 @@
+jdk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/ant.xml Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="AntConfiguration">
+ <buildFile url="file://$PROJECT_DIR$/.idea/build.xml">
+ <properties>
+ <property name="boot.java.home" value="$JDKPath$" />
+ <property name="jtreg.tests" value="$FilePath$" />
+ <property name="jtreg.home" value="###" />
+ <property name="build.target.dir" value="specDir" /> <!-- this will be replaced -->
+ <property name="jtreg.jpda.jvmargs" value="-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:5900,suspend=y" />
+ <property name="module.name" value="java.base" /> <!-- this will be replaced -->
+ </properties>
+ <executeOn event="afterCompilation" target="post-make" />
+ </buildFile>
+ </component>
+</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/build.xml Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,126 @@
+<!-- importing.xml -->
+<project name="jdk" basedir="..">
+
+ <script language="javascript" classpath=".idea/classes">
+ var JdkLogger = Java.type("idea.JdkIdeaAntLogger");
+ new JdkLogger(project)
+ </script>
+
+ <!-- java.marker is set to a marker file to check for within a Java install dir.
+ The best file to check for across Solaris/Linux/Windows/MacOS is one of the
+ executables; regrettably, that is OS-specific. -->
+ <condition property="java.marker" value="bin/java">
+ <os family="unix"/>
+ </condition>
+ <condition property="java.marker" value="bin/java.exe">
+ <os family="windows"/>
+ </condition>
+
+ <property name="test.dir" value="${basedir}/jdk/test"/>
+
+ <macrodef name="call-make">
+ <attribute name="dir"/>
+ <attribute name="args"/>
+ <sequential>
+ <exec executable="make" dir="@{dir}" failonerror="true">
+ <arg line="@{args}"/>
+ <env key="CLASSPATH" value = ""/>
+ </exec>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="exec-target">
+ <attribute name="antfile" default="${ant.file}" />
+ <attribute name="target" />
+ <sequential>
+ <java classname="org.apache.tools.ant.Main" fork="true" spawn="true">
+ <arg value="-f"/>
+ <arg value="@{antfile}"/>
+ <arg value="-Dboot.java.home=${boot.java.home}"/>
+ <arg value="-Dbuild.target.dir=${build.target.dir}"/>
+ <arg value="-Djtreg.home=${jtreg.home}"/>
+ <arg value="-Djtreg.tests=${jtreg.tests}"/>
+ <arg value="-Djtreg.jpda.jvmargs=${jtreg.jpda.jvmargs}"/>
+ <arg value="@{target}"/>
+ <classpath>
+ <pathelement path="${java.class.path}"/>
+ </classpath>
+ </java>
+ </sequential>
+ </macrodef>
+
+ <target name="post-make" depends="build-module"/>
+
+ <!--
+ **** Global JDK Build Targets
+ -->
+
+ <target name="clean" depends="-do-configure">
+ <echo message="base = ${basedir}"/>
+ <call-make dir = "${build.target.dir}" args = "clean"/>
+ </target>
+
+ <target name="-do-configure">
+ <echo message="base = ${basedir}"/>
+ <fail message="Not part of a full JDK forest">
+ <condition>
+ <not>
+ <available file="${basedir}/configure" />
+ </not>
+ </condition>
+ </fail>
+ <exec executable="sh" dir="${basedir}" failonerror="true">
+ <arg line="configure --with-boot-jdk=${boot.java.home}"/>
+ </exec>
+ </target>
+
+ <target name="images">
+ <call-make dir = "${build.target.dir}" args = "images"/>
+ </target>
+
+ <target name="jimages">
+ <call-make dir = "${build.target.dir}" args = "jimages"/>
+ </target>
+
+ <target name="check-env">
+ <exec executable="env" dir="${basedir}"/>
+ </target>
+
+ <target name="build-module">
+ <call-make dir = "${build.target.dir}" args = "${module.name}"/>
+ </target>
+
+ <target name="-check-boot.java.home" depends="-def-check">
+ <check name="bootstrap java" property="boot.java.home" marker="${java.marker}"/>
+ </target>
+
+ <target name="-def-check">
+ <macrodef name="check">
+ <attribute name="name"/>
+ <attribute name="property"/>
+ <attribute name="marker" default=""/>
+ <sequential>
+ <fail message="Cannot locate @{name}: please set @{property} to its location">
+ <condition>
+ <not>
+ <isset property="@{property}"/>
+ </not>
+ </condition>
+ </fail>
+ <fail message="@{name} is not installed in ${@{property}}">
+ <condition>
+ <and>
+ <not>
+ <equals arg1="@{marker}" arg2=""/>
+ </not>
+ <not>
+ <available file="${@{property}}/@{marker}"/>
+ </not>
+ </and>
+ </condition>
+ </fail>
+ </sequential>
+ </macrodef>
+ </target>
+</project>
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/compiler.xml Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <option name="DEFAULT_COMPILER" value="Javac" />
+ <excludeFromCompile>
+ <directory url="file://$PROJECT_DIR$/jdk/src" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/langtools/src" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/jaxp/src" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/jaxws/src" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/corba/src" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/nashorn/src" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/build" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/jdk/test" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/jaxp/test" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/langtools/test" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/langtools" includeSubdirectories="true" />
+ </excludeFromCompile>
+ <resourceExtensions />
+ <wildcardResourcePatterns>
+ <entry name="!?*.java" />
+ <entry name="!?*.form" />
+ <entry name="!?*.class" />
+ <entry name="!?*.groovy" />
+ <entry name="!?*.scala" />
+ <entry name="!?*.flex" />
+ <entry name="!?*.kt" />
+ <entry name="!?*.clj" />
+ </wildcardResourcePatterns>
+ <annotationProcessing>
+ <profile default="true" name="Default" enabled="false">
+ <processorPath useClasspath="true" />
+ </profile>
+ </annotationProcessing>
+ </component>
+</project>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/copyright/profiles_settings.xml Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,3 @@
+<component name="CopyrightManager">
+ <settings default="" />
+</component>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/jdk.iml Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/####" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/jdk/test" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/langtools/test" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/jaxp/test" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/####/buildtools" />
+ <excludeFolder url="file://$MODULE_DIR$/####/configure-support" />
+ <excludeFolder url="file://$MODULE_DIR$/####/hotspot" />
+ <excludeFolder url="file://$MODULE_DIR$/####/images" />
+ <excludeFolder url="file://$MODULE_DIR$/####/ide" />
+ <excludeFolder url="file://$MODULE_DIR$/####/jdk" />
+ <excludeFolder url="file://$MODULE_DIR$/####/make-support" />
+ <excludeFolder url="file://$MODULE_DIR$/####/testoutput" />
+ </content>
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="inheritedJdk" />
+ </component>
+</module>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/misc.xml Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="EntryPointsManager">
+ <entry_points version="2.0" />
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_9" assert-keyword="true" jdk-15="true">
+ <output url="file://$PROJECT_DIR$/build/idea/out" />
+ </component>
+</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/modules.xml Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/.idea/jdk.iml" filepath="$PROJECT_DIR$/.idea/jdk.iml" />
+ </modules>
+ </component>
+</project>
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/scopes/scope_settings.xml Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,5 @@
+<component name="DependencyValidationManager">
+ <state>
+ <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+ </state>
+</component>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/src/idea/JdkIdeaAntLogger.java Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,373 @@
+/*
+ * 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.BuildListener;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+
+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 JdkIdeaAntLogger extends DefaultLogger {
+
+ /**
+ * This is just a way to pass in customized binary string predicates;
+ *
+ * TODO: replace with @code{BiPredicate<String, String>} 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);
+ }
+ },
+ MATCHES {
+ @Override
+ boolean apply(String s1, String s2) {
+ return s1.matches(s2);
+ }
+ };
+
+ abstract boolean apply(String s1, String s2);
+ }
+
+ /**
+ * Various kinds of ant messages that we shall intercept
+ */
+ enum MessageKind {
+
+ /** a make error */
+ MAKE_ERROR(StringBinaryPredicate.CONTAINS, MSG_ERR, "error:", "compiler.err"),
+ /** a make warning */
+ MAKE_WARNING(StringBinaryPredicate.CONTAINS, MSG_WARN, "warning:", "compiler.warn"),
+ /** a make note */
+ MAKE_NOTE(StringBinaryPredicate.CONTAINS, MSG_INFO, "note:", "compiler.note"),
+ /** std make output */
+ MAKE_OTHER(StringBinaryPredicate.MATCHES, 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 */
+ MAKE("exec", MessageKind.MAKE_ERROR, MessageKind.MAKE_WARNING, MessageKind.MAKE_NOTE,
+ MessageKind.MAKE_OTHER, 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 selected modules */
+ BUILD_MODULE("build-module") {
+ @Override
+ String getDisplayMessage(BuildEvent e) {
+ return "Building modules: " + e.getProject().getProperty("module.name") + "...";
+ }
+ },
+ /** build images */
+ BUILD_IMAGES("images") {
+ @Override
+ String getDisplayMessage(BuildEvent e) {
+ return "Building images...";
+ }
+ },
+ /** build images */
+ CONFIGURE("-do-configure") {
+ @Override
+ String getDisplayMessage(BuildEvent e) {
+ return "Configuring build...";
+ }
+ },
+ /** 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<StatusEvent> statusEvents = new Stack<>();
+
+ /** stack of pending tasks */
+ Stack<Task> tasks = new Stack<>();
+
+ public JdkIdeaAntLogger(Project project) {
+ for (Object o : project.getBuildListeners()) {
+ if (o instanceof DefaultLogger) {
+ this.logger = (DefaultLogger)o;
+ project.removeBuildListener((BuildListener)o);
+ project.addBuildListener(this);
+ }
+ }
+ 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<Target> 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();
+ System.err.println("task started " + taskName);
+ 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);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/vcs.xml Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$" vcs="hg4idea" />
+ <mapping directory="$PROJECT_DIR$/jdk" vcs="hg4idea" />
+ <mapping directory="$PROJECT_DIR$/langtools" vcs="hg4idea" />
+ <mapping directory="$PROJECT_DIR$/hotspot" vcs="hg4idea" />
+ <mapping directory="$PROJECT_DIR$/nashorn" vcs="hg4idea" />
+ <mapping directory="$PROJECT_DIR$/jaxp" vcs="hg4idea" />
+ <mapping directory="$PROJECT_DIR$/jaxws" vcs="hg4idea" />
+ <mapping directory="$PROJECT_DIR$/corba" vcs="hg4idea" />
+ </component>
+</project>
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/idea/template/workspace.xml Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ChangeListManager">
+ <ignored path="jdk.iws" />
+ <ignored path="$PROJECT_DIR$/build/idea/out/" />
+ <ignored path=".idea/" />
+ </component>
+ <component name="StructureViewFactory">
+ <option name="ACTIVE_ACTIONS" value=",ALPHA_COMPARATOR" />
+ </component>
+ <component name="antWorkspaceConfiguration">
+ <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
+ <option name="FILTER_TARGETS" value="false" />
+ <buildFile url="file://$PROJECT_DIR$/.idea/build.xml">
+ <runInBackground value="false" />
+ <targetFilters>
+ <filter targetName="post-make" isVisible="false" />
+ <filter targetName="clean" isVisible="true" />
+ <filter targetName="-do-configure" isVisible="false" />
+ <filter targetName="images" isVisible="true" />
+ <filter targetName="build-module" isVisible="true" />
+ <filter targetName="jtreg-debug" isVisible="false" />
+ <filter targetName="jtreg-debug-internal" isVisible="false" />
+ <filter targetName="jtreg" isVisible="false" />
+ <filter targetName="-check-jtreg.home" isVisible="false" />
+ <filter targetName="-def-check" isVisible="false" />
+ <filter targetName="-def-jtreg" isVisible="false" />
+ <filter targetName="-check-boot.java.home" isVisible="false" />
+ <filter targetName="-check-target.java.home" isVisible="false" />
+ <filter targetName="find-jdk-build-dir" isVisible="false" />
+ <filter targetName="check-env" isVisible="false" />
+ </targetFilters>
+ <treeView value="false" />
+ <expanded value="true" />
+ </buildFile>
+ </component>
+ <component name="ProjectView">
+ <navigator currentView="PackagesPane" proportions="" version="1">
+ <flattenPackages />
+ <showMembers />
+ <showModules />
+ <showLibraryContents />
+ <hideEmptyPackages />
+ <abbreviatePackageNames />
+ <autoscrollToSource />
+ <autoscrollFromSource />
+ <sortByType />
+ </navigator>
+ <panes>
+ <pane id="ProjectPane">
+ <subPane>
+ <PATH>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="jdk" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+ </PATH_ELEMENT>
+ </PATH>
+ </subPane>
+ </pane>
+ <pane id="PackagesPane">
+ <subPane>
+ <PATH>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="jdk" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="jdk" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" />
+ </PATH_ELEMENT>
+ </PATH>
+ </subPane>
+ </pane>
+ <pane id="Scope" />
+ </panes>
+ </component>
+</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/make/TestIdea.gmk Tue May 10 13:34:30 2016 +0100
@@ -0,0 +1,46 @@
+
+# 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.
+#
+
+include $(SPEC)
+include MakeBase.gmk
+
+default: all
+
+IDEA_OUTPUT_DIR := $(TESTMAKE_OUTPUTDIR)/verify-idea
+
+clean-idea:
+ $(RM) -r $(IDEA_OUTPUT_DIR)
+
+verify-idea:
+ $(MKDIR) -p $(IDEA_OUTPUT_DIR)
+ $(BASH) $(TOPDIR)/common/bin/idea.sh -o $(IDEA_OUTPUT_DIR)/idea1
+ $(BASH) $(TOPDIR)/common/bin/idea.sh -o $(IDEA_OUTPUT_DIR)/idea2 java.base
+ $(BASH) $(TOPDIR)/common/bin/idea.sh -o $(IDEA_OUTPUT_DIR)/idea3 java.base jdk.compiler
+
+TEST_TARGETS += verify-idea
+
+all: $(TEST_TARGETS)
+
+.PHONY: default all verify-idea
--- a/test/make/TestMake.gmk Wed Jul 05 21:41:01 2017 +0200
+++ b/test/make/TestMake.gmk Tue May 10 13:34:30 2016 +0100
@@ -33,7 +33,10 @@
java-compilation:
+$(MAKE) -f TestJavaCompilation.gmk $(TEST_SUBTARGET)
+test-idea:
+ +$(MAKE) -f TestIdea.gmk $(TEST_SUBTARGET)
-all: make-base java-compilation
-.PHONY: default all make-base java-compilation
+all: make-base java-compilation test-idea
+
+.PHONY: default all make-base java-compilation test-idea