diff --git a/build.xml b/build.xml
index dd8c90d39069..bafc55b124a3 100644
--- a/build.xml
+++ b/build.xml
@@ -33,7 +33,16 @@
-
+
+
+
+
+
+
+
+
+
+
diff --git a/build/download_kotlin.xml b/build/download_kotlin.xml
index 47259dcbc870..3a47354116c8 100644
--- a/build/download_kotlin.xml
+++ b/build/download_kotlin.xml
@@ -8,6 +8,8 @@
+
+
diff --git a/build/scripts/download_kotlin.gant b/build/scripts/download_kotlin.gant
new file mode 100644
index 000000000000..f8b7184fbbff
--- /dev/null
+++ b/build/scripts/download_kotlin.gant
@@ -0,0 +1,168 @@
+import com.intellij.util.text.VersionComparatorUtil
+
+import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+includeTargets << new File("${guessHome(this)}/build/scripts/utils.gant")
+
+String kotlinBuildConfigurationOnPublicTeamCity = "Kotlin_M15_2_Idea142branch150versionNoTests"
+
+target('default': 'Ensures that build/kotlinc directory contains JARs from Kotlin plugin compatible with current IDEA sources. These JARs are used by build scripts.') {
+ def communityHome = guessHome(this)
+ def reasonToUpdate = getReasonToUpdate(communityHome, new File(communityHome, "build/kotlinc"))
+ if (reasonToUpdate == null) {
+ projectBuilder.info("Compatible Kotlin plugin already installed, no update is required")
+ return
+ }
+
+ projectBuilder.info("Kotlin plugin will be updated: $reasonToUpdate")
+
+ if (isDefined("workIdeaHome")) {
+ if (updatePluginFromWorkIdeaInstallation(communityHome, workIdeaHome)) {
+ return
+ }
+ }
+
+ if (isDefined("kotlin.teamcity.host")) {
+ if (downloadAndExtract(new TeamCityBuildLocator(p("kotlin.teamcity.host"), p("kotlin.build.type.id"), p("kotlin.build.selector")), communityHome)) {
+ return
+ }
+ }
+ if (!downloadAndExtract(new TeamCityBuildLocator("https://teamcity.jetbrains.com", kotlinBuildConfigurationOnPublicTeamCity, "status:SUCCESS"), communityHome)) {
+ projectBuilder.error("Failed to install Kotlin plugin")
+ }
+}
+
+private boolean updatePluginFromWorkIdeaInstallation(String communityHome, String workIdeaHome) {
+ def kotlinPluginFromWorkIdea = new File(workIdeaHome, "plugins/Kotlin")
+ if (!kotlinPluginFromWorkIdea.exists()) {
+ projectBuilder.info("Cannot use Kotlin from $workIdeaHome: the plugin directory doesn't exist")
+ return false
+ }
+
+ def reasonToUpdate = getReasonToUpdate(communityHome, new File(kotlinPluginFromWorkIdea, "kotlinc"))
+ if (reasonToUpdate != null) {
+ projectBuilder.info("Cannot use Kotlin from $workIdeaHome: $reasonToUpdate")
+ return false
+ }
+
+ ant.delete(dir: "$communityHome/build/kotlinc")
+ ant.copy(todir: "$communityHome/build/kotlinc/plugin/Kotlin") {
+ ant.fileset(dir: kotlinPluginFromWorkIdea)
+ }
+ ant.copy(todir: "$communityHome/build/kotlinc") {
+ ant.fileset(dir: "$kotlinPluginFromWorkIdea/kotlinc")
+ }
+ ant.copy(todir: "$communityHome/build/kotlinc/jps") {
+ ant.fileset(dir: "$kotlinPluginFromWorkIdea/lib/jps")
+ }
+ projectBuilder.info("Kotlin plugin updated from $workIdeaHome installation")
+ return true
+}
+
+private String getReasonToUpdate(String communityHome, File kotlincHome) {
+ def buildTxt = new File(kotlincHome, "build.txt")
+ if (!buildTxt.exists()) {
+ return "Kotlin binaries aren't found at $kotlincHome"
+ }
+
+ String currentKotlinVersion
+ try {
+ currentKotlinVersion = buildTxt.text
+ }
+ catch (IOException e) {
+ return "cannot read Kotlin version from $buildTxt.absolutePath: $e"
+ }
+
+ def externalDependenciesXml = new File(communityHome, ".idea/externalDependencies.xml")
+ if (!externalDependenciesXml.exists()) {
+ return "cannot read required Kotlin version from $externalDependenciesXml.absolutePath: the file doesn't exist"
+ }
+
+ def root = new XmlParser().parse(externalDependenciesXml)
+ def pluginTag = root.component?.find { it.@name == "ExternalDependencies" }?.plugin?.find { it.@id == "org.jetbrains.kotlin" }
+ if (pluginTag == null) return "cannot find Kotlin plugin in $externalDependenciesXml.absolutePath"
+
+ String minVersion = pluginTag['@min-version']
+ if (minVersion == null) return "minimum required version for Kotlin plugin isn't specified in $externalDependenciesXml.absolutePath"
+ String maxVersion = pluginTag['@max-version']
+ if (maxVersion == null) return "maximum required version for Kotlin plugin isn't specified in $externalDependenciesXml.absolutePath"
+
+ if (VersionComparatorUtil.compare(currentKotlinVersion, minVersion) < 0) {
+ return "current Kotlin version is $currentKotlinVersion, but at least $minVersion is required";
+ }
+ if (VersionComparatorUtil.compare(currentKotlinVersion, maxVersion) > 0) {
+ return "current Kotlin version is $currentKotlinVersion, but at most $maxVersion is required";
+ }
+ return null
+}
+
+private boolean downloadAndExtract(TeamCityBuildLocator buildLocator, String communityHome) {
+ String buildNumber;
+ try {
+ buildNumber = new URL(buildLocator.buildNumberUrl).text
+ }
+ catch (Exception e) {
+ projectBuilder.info("Cannot get Kotlin build number from $buildLocator.buildNumberUrl: $e")
+ return false
+ }
+
+
+ def pluginZip = new File(communityHome, "build/kotlin-plugin-${buildNumber}.zip")
+ def pluginZipPath = pluginZip.absolutePath
+ if (!pluginZip.exists()) {
+ ant.delete(dir: "$communityHome/build", includes: "kotlin-plugin-*.zip")
+ projectBuilder.info("Downloading Kotlin build $buildNumber")
+ ant.get(src: buildLocator.getDownloadUrl(buildNumber), dest: pluginZipPath)
+ }
+ else {
+ projectBuilder.info("Extracting Kotlin build $buildNumber")
+ }
+
+ def kotlincDir = "$communityHome/build/kotlinc"
+ ant.delete(dir: kotlincDir)
+ ant.unzip(src: pluginZipPath, dest: "$kotlincDir/plugin")
+ ant.unzip(src: pluginZipPath, dest: "$kotlincDir") {
+ ant.patternset(includes: "Kotlin/kotlinc/**")
+ ant.globmapper(from: "Kotlin/kotlinc/*", to: "*")
+ }
+ ant.unzip(src: pluginZipPath, dest: "$kotlincDir/jps") {
+ ant.patternset(includes: "Kotlin/lib/jps/**")
+ ant.globmapper(from: "Kotlin/lib/jps/*", to: "*")
+ }
+ return true
+}
+
+class TeamCityBuildLocator {
+ String host;
+ String buildTypeId;
+ String buildSelector;
+
+ TeamCityBuildLocator(String host, String buildTypeId, String buildSelector) {
+ this.host = host
+ this.buildTypeId = buildTypeId
+ this.buildSelector = buildSelector
+ }
+
+ String getBuildNumberUrl() {
+ return "$host/guestAuth/app/rest/buildTypes/id:$buildTypeId/builds/$buildSelector,count:1/number"
+ }
+
+ String getDownloadUrl(String buildNumber) {
+ String encodedBuildNumber = URLEncoder.encode(buildNumber, "UTF-8")
+ return "$host/guestAuth/repository/download/$buildTypeId/$encodedBuildNumber/kotlin-plugin-${encodedBuildNumber}.zip"
+ }
+}
diff --git a/build/update.cmd b/build/update.cmd
index 558db879aecc..1fe5808b31d9 100644
--- a/build/update.cmd
+++ b/build/update.cmd
@@ -30,7 +30,7 @@ CD "%DEV_IDEA_HOME%"
SET ANT_HOME=%DEV_IDEA_HOME%\lib\ant
SET EXEC_ANT="%JAVA_HOME%\bin\java.exe" -Dant.home="%ANT_HOME%" -classpath "%ANT_HOME%\lib\ant-launcher.jar" org.apache.tools.ant.launch.Launcher
-%EXEC_ANT% -f build/update.xml
+%EXEC_ANT% -f build/update.xml -Dwork.idea.home="%WORK_IDEA_HOME%"
IF NOT ERRORLEVEL 0 GOTO failed
IF NOT EXIST "%DEV_IDEA_HOME%\out\deploy" GOTO failed
diff --git a/build/update.sh b/build/update.sh
index 22351f649b60..f1cfe923f16a 100755
--- a/build/update.sh
+++ b/build/update.sh
@@ -27,7 +27,7 @@ echo "Updating $WORK_IDEA_HOME from compiled classes in $DEV_IDEA_HOME"
ANT_HOME="$DEV_IDEA_HOME/lib/ant"
ANT_CLASSPATH="$DEV_IDEA_HOME/build/lib/gant/lib/jps.jar"
java -Xms64m -Xmx512m -Dant.home="$ANT_HOME" -classpath "$ANT_HOME/lib/ant-launcher.jar" org.apache.tools.ant.launch.Launcher \
- -lib "$ANT_CLASSPATH" -f "$DEV_IDEA_HOME/build/update.xml" $TARGET
+ -lib "$ANT_CLASSPATH" -f "$DEV_IDEA_HOME/build/update.xml" -Dwork.idea.home="$WORK_IDEA_HOME" $TARGET
if [ "$?" != "0" ]; then
echo "Update failed; work IDEA build not modified."
diff --git a/build/update.xml b/build/update.xml
index adbc5c5faa0b..84f918f3fa0a 100644
--- a/build/update.xml
+++ b/build/update.xml
@@ -25,6 +25,17 @@
+
+
+
+
+
+
+
+
+
+
+