diff --git a/compiler/compiler.pro b/compiler/compiler.pro index 323072c06c4..eb98a2cffd3 100644 --- a/compiler/compiler.pro +++ b/compiler/compiler.pro @@ -93,7 +93,6 @@ messages/**) # for gradle plugin and other server tools -keep class com.intellij.openapi.util.io.ZipFileCache { public *; } --keep class com.intellij.openapi.util.LowMemoryWatcher { public *; } # for j2k -keep class com.intellij.codeInsight.NullableNotNullManager { public protected *; } diff --git a/libraries/examples/kotlin-gradle-subplugin-example/pom.xml b/libraries/examples/kotlin-gradle-subplugin-example/pom.xml index d2553cece8f..aed881a7690 100644 --- a/libraries/examples/kotlin-gradle-subplugin-example/pom.xml +++ b/libraries/examples/kotlin-gradle-subplugin-example/pom.xml @@ -41,7 +41,7 @@ org.jetbrains.kotlin - kotlin-compiler + kotlin-compiler-embeddable ${project.version} provided diff --git a/libraries/pom.xml b/libraries/pom.xml index c497fef5835..d2047e03b5c 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -72,6 +72,7 @@ tools/kotlin-compiler + tools/kotlin-compiler-embeddable tools/kotlin-jdk-annotations tools/kotlin-android-sdk-annotations tools/kotlin-maven-plugin diff --git a/libraries/tools/kotlin-compiler-embeddable/pom.xml b/libraries/tools/kotlin-compiler-embeddable/pom.xml new file mode 100644 index 00000000000..7604f7f374b --- /dev/null +++ b/libraries/tools/kotlin-compiler-embeddable/pom.xml @@ -0,0 +1,165 @@ + + + + 4.0.0 + + 1.4.1 + 3.0.4 + org.jetbrains.kotlin.relocated + + + + org.jetbrains.kotlin + kotlin-project + 0.1-SNAPSHOT + ../../pom.xml + + + kotlin-compiler-embeddable + jar + + the Kotlin compiler embeddable + + + + org.jetbrains.kotlin + kotlin-compiler + ${project.version} + + + org.jetbrains.kotlin + kotlin-runtime + ${project.version} + + + org.jetbrains.kotlin + kotlin-reflect + ${project.version} + + + org.jetbrains.kotlin + kotlin-stdlib + ${project.version} + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.7 + + + attach-artifacts + compile + + attach-artifact + + + + + ${kotlin-dist}/kotlin-compiler-sources.jar + jar + sources + + + ${kotlin-dist}/kotlin-compiler-javadoc.jar + jar + javadoc + + + + + + + attach-empty-javadoc + prepare-package + + attach-artifact + + + true + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.4.1 + + + package + + shade + + + false + true + + + *:kotlin-runtime + + META-INF/** + + *:kotlin-reflect + + META-INF/** + + *:kotlin-stdlib + + META-INF/** + + + + + + com.google + ${kotlin.relocated.package}.com.google + + + com.sampullara + ${kotlin.relocated.package}.com.sampullara + + + org.apache + ${kotlin.relocated.package}.org.apache + + + org.jdom + ${kotlin.relocated.package}.org.jdom + + + org.fusesource + ${kotlin.relocated.package}.org.fusesource + + + org.picocontainer + ${kotlin.relocated.package}.org.picocontainer + + + jline + ${kotlin.relocated.package}.jline + + + gnu + ${kotlin.relocated.package}.gnu + + + javax.inject + ${kotlin.relocated.package}.javax.inject + + + + + + + + + diff --git a/libraries/tools/kotlin-gradle-plugin-core/pom.xml b/libraries/tools/kotlin-gradle-plugin-core/pom.xml index 8636d5d7c2a..62c431995ac 100644 --- a/libraries/tools/kotlin-gradle-plugin-core/pom.xml +++ b/libraries/tools/kotlin-gradle-plugin-core/pom.xml @@ -44,7 +44,7 @@ org.jetbrains.kotlin - kotlin-compiler + kotlin-compiler-embeddable ${project.version} diff --git a/libraries/tools/kotlin-gradle-plugin-core/src/main/kotlin/com/intellij/openapi/util/LowMemoryWatcher.java b/libraries/tools/kotlin-gradle-plugin-core/src/main/kotlin/com/intellij/openapi/util/LowMemoryWatcher.java deleted file mode 100644 index 654b563f796..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-core/src/main/kotlin/com/intellij/openapi/util/LowMemoryWatcher.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2000-2013 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. - */ -package com.intellij.openapi.util; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.util.ConcurrencyUtil; -import com.intellij.util.containers.WeakList; -import org.jetbrains.annotations.NotNull; - -import javax.management.ListenerNotFoundException; -import javax.management.Notification; -import javax.management.NotificationEmitter; -import javax.management.NotificationListener; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryNotificationInfo; -import java.lang.management.MemoryPoolMXBean; -import java.lang.management.MemoryType; -import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * PATCHED VERSION of com.intellij.openapi.util.LowMemoryWatcher - * shutdown() method added to stop background thread. - */ -@SuppressWarnings("UnusedDeclaration") -public class LowMemoryWatcher { - private static final long MEM_THRESHOLD = 5 /*MB*/ * 1024 * 1024; - - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.LowMemoryWatcher"); - - private static final List ourInstances = new WeakList(); - private static final ThreadPoolExecutor ourExecutor = new ThreadPoolExecutor(0, 1, 10, TimeUnit.SECONDS, new ArrayBlockingQueue(2), ConcurrencyUtil.newNamedThreadFactory("LowMemoryWatcher janitor")); - private static boolean ourSubmitted; - private static final Runnable ourJanitor = new Runnable() { - @Override - public void run() { - try { - for (LowMemoryWatcher watcher : ourInstances) { - try { - watcher.myRunnable.run(); - } - catch (Throwable e) { - LOG.info(e); - } - } - } - finally { - synchronized (ourJanitor) { - //noinspection AssignmentToStaticFieldFromInstanceMethod - ourSubmitted = false; - } - } - } - }; - - private final Runnable myRunnable; - - private static final NotificationListener lowMemoryListener; - - static { - for (MemoryPoolMXBean bean : ManagementFactory.getMemoryPoolMXBeans()) { - if (bean.getType() == MemoryType.HEAP && bean.isUsageThresholdSupported()) { - long threshold = bean.getUsage().getMax() - MEM_THRESHOLD; - if (threshold > 0) { - bean.setUsageThreshold(threshold); - bean.setCollectionUsageThreshold(threshold); - } - } - } - - lowMemoryListener = new NotificationListener() { - @Override - public void handleNotification(@NotNull Notification n, Object hb) { - if (MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED.equals(n.getType()) || - MemoryNotificationInfo.MEMORY_COLLECTION_THRESHOLD_EXCEEDED.equals(n.getType())) { - synchronized (ourJanitor) { - if (!ourSubmitted) { - //noinspection AssignmentToStaticFieldFromInstanceMethod - ourSubmitted = true; - ourExecutor.submit(ourJanitor); - } - } - } - } - }; - - ((NotificationEmitter)ManagementFactory.getMemoryMXBean()).addNotificationListener(lowMemoryListener, null, null); - } - - /** - * Registers a runnable to run on low memory events - * @return a LowMemoryWatcher instance holding the runnable. This instance should be kept in memory while the - * low memory notification functionality is needed. As soon as it's garbage-collected, the runnable won't receive any further notifications. - */ - public static LowMemoryWatcher register(Runnable runnable) { - return new LowMemoryWatcher(runnable); - } - - /** - * Registers a runnable to run on low memory events. The notifications will be issued until parentDisposable is disposed. - */ - public static void register(Runnable runnable, Disposable parentDisposable) { - final Ref watcher = Ref.create(new LowMemoryWatcher(runnable)); - Disposer.register(parentDisposable, new Disposable() { - @Override - public void dispose() { - watcher.get().stop(); - watcher.set(null); - } - }); - } - - private LowMemoryWatcher(Runnable runnable) { - myRunnable = runnable; - ourInstances.add(this); - } - - public void stop() { - ourInstances.remove(this); - } - - /** - * LowMemoryWatcher maintains a background thread where all the handlers are invoked. - * In server environments, this thread may run indefinitely and prevent the class loader from - * being gc-ed. Thus it's necessary to invoke this method to stop that thread and let the classes be garbage-collected. - */ - public static void stopAll() throws InterruptedException, ListenerNotFoundException { - ourInstances.clear(); - ((NotificationEmitter)ManagementFactory.getMemoryMXBean()).removeNotificationListener(lowMemoryListener); - - ourExecutor.shutdown(); - - boolean terminated = ourExecutor.awaitTermination(50, TimeUnit.MICROSECONDS); - if (!terminated) { - ourExecutor.shutdownNow(); - } - } -} diff --git a/libraries/tools/kotlin-gradle-plugin/pom.xml b/libraries/tools/kotlin-gradle-plugin/pom.xml index 9252c989dd8..038e68f14fe 100644 --- a/libraries/tools/kotlin-gradle-plugin/pom.xml +++ b/libraries/tools/kotlin-gradle-plugin/pom.xml @@ -41,7 +41,6 @@ org.jetbrains.kotlin kotlin-gradle-plugin-core ${project.version} - test org.jetbrains.kotlin diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/FinishBuildListener.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/FinishBuildListener.kt index f6039acb826..55591175725 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/FinishBuildListener.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/FinishBuildListener.kt @@ -18,64 +18,85 @@ package org.jetbrains.kotlin.gradle.plugin import org.gradle.BuildAdapter import org.gradle.BuildResult +import org.gradle.api.invocation.Gradle import org.gradle.api.logging.Logging import java.lang.ref.Reference import java.util.concurrent.ScheduledExecutorService +import kotlin.text.MatchGroup -class FinishBuildListener(var pluginClassLoader: ParentLastURLClassLoader?) : BuildAdapter() { +internal fun getUsedMemoryKb(): Long { + System.gc() + val rt = Runtime.getRuntime() + return (rt.totalMemory() - rt.freeMemory()) / 1024 +} + + +private fun comparableVersionStr(version: String) = + "(\\d+)\\.(\\d+).*" + .toRegex() + .match(version) + ?.groups + ?.drop(1)?.take(2) + // checking if two subexpression groups are found and length of each is >0 and <4 + ?.let { if (it.all { (it?.value?.length() ?: 0).let { it > 0 && it < 4 }}) it else null } + ?.joinToString(".", transform = { it!!.value.padStart(3, '0') }) + + +class FinishBuildListener(pluginClassLoader: ClassLoader?, val startMemory: Long) : BuildAdapter() { val log = Logging.getLogger(this.javaClass) private var threadTracker: ThreadTracker? = ThreadTracker() + private val cleanup = CompilerServicesCleanup(pluginClassLoader) + override fun buildFinished(result: BuildResult?) { log.kotlinDebug("Build finished listener") - stopZipFileCache() - stopLowMemoryWatcher() - stopJobScheduler() - - // TODO: Try to clean up thread locals without this ugly hack - // TODO: Further investigation of PermGen leak (KT-6451) - removeThreadLocals() - - pluginClassLoader = null - result?.getGradle()?.removeListener(this) - - threadTracker?.checkThreadLeak(result?.getGradle()) - threadTracker = null - } - - public fun removeThreadLocals() { - try { - log.kotlinDebug("Remove ChildURLClassLoader thread locals") - - val thread = Thread.currentThread() - val threadLocalsField = javaClass().getDeclaredField("threadLocals") - threadLocalsField.setAccessible(true) - - val threadLocalMapClass = Class.forName("java.lang.ThreadLocal\$ThreadLocalMap") - val tableField = threadLocalMapClass.getDeclaredField("table") - tableField.setAccessible(true) - - val referentField = javaClass>().getDeclaredField("referent") - referentField.setAccessible(true) - - val table = tableField[threadLocalsField[thread]] as Array<*> - - for (entry in table) { - if (entry != null) { - val threadLocal = referentField[entry] as ThreadLocal<*>? - val classLoader = threadLocal?.javaClass?.getClassLoader() - if (classLoader is ParentLastURLClassLoader.ChildURLClassLoader) { - threadLocal?.remove() - } + val gradle = result?.getGradle() + if (gradle != null) { + // making cleanup only on recognized versions of gradle and if version < 2.4 + // otherwise it may cause problems e.g. with JobScheduler on subsequent runs + // this strategy may lead to memory leaks, but prevent crashes due to destroyed JobScheduler + // the reason for the strategy is the following: + // gradle < 2.4 has problems with plugin reuse in the daemon: new calls to the plugin are made with a new classloader + // for every new build. With statically initialized daemons like JobScheduler that leads to big leaks of classloaders and classes, + // therefore to reduce leaks JobScheduler (and deprecated ZipFileCache for now) should be stopped) + // It should be noted that because of this behavior there are no benefits of using daemon in these versions. + // Starting from 2.4 gradle using cached classloaders, that leads to effective class reusing in the daemon, but + // in that case premature stopping of the static daemons may lead to crashes. + comparableVersionStr(gradle.getGradleVersion())?.let { + log.kotlinDebug("detected gradle version $it") + if (it < comparableVersionStr("2.4")!!) { + cleanup() + // checking thread leaks only then cleaning up + threadTracker?.checkThreadLeak(gradle) } } - log.kotlinDebug("Removing ChildURLClassLoader thread locals finished successfully") - } catch (e: Throwable) { - log.kotlinDebug("Exception during thread locals remove: " + e) + threadTracker = null + gradle.removeListener(this) } + + // the value reported here is not necessarily a leak, since it is calculated before collecting the plugin classes + // but on subsequent runs in the daemon it should be rather small, then the classes are actually reused by the daemon (see above) + getUsedMemoryKb().let { log.kotlinDebug("[PERF] Used memory after build: $it kb (${"%+d".format(it - startMemory)} kb)") } + } +} + + +class CompilerServicesCleanup(private var pluginClassLoader: ClassLoader?) { + val log = Logging.getLogger(this.javaClass) + + fun invoke() { + assert(pluginClassLoader != null) + + log.kotlinDebug("compiler services cleanup") + + // TODO: remove ZipFileCache cleanup after switching to recent idea libs + stopZipFileCache() + stopJobScheduler() + + pluginClassLoader = null } private fun stopZipFileCache() { @@ -83,11 +104,6 @@ class FinishBuildListener(var pluginClassLoader: ParentLastURLClassLoader?) : Bu log.kotlinDebug("ZipFileCache finished successfully") } - private fun stopLowMemoryWatcher() { - callVoidStaticMethod("com.intellij.openapi.util.LowMemoryWatcher", "stopAll") - log.kotlinDebug("LowMemoryWatcher finished successfully") - } - private fun stopJobScheduler() { log.kotlinDebug("Stop JobScheduler") diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index d03daf91acf..5ba80e0f296 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -13,12 +13,18 @@ import org.gradle.api.invocation.Gradle import org.gradle.api.logging.Logger import java.lang.reflect.Method import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider +import java.net.URLClassLoader + +// TODO: simplify: the complicated structure is a leftover from dynamic loading of plugin core, could be significantly simplified now abstract class KotlinBasePluginWrapper: Plugin { + val log = Logging.getLogger(this.javaClass) public override fun apply(project: Project) { + val startMemory = getUsedMemoryKb() + val sourceBuildScript = findSourceBuildScript(project) if (sourceBuildScript == null) { log.error("Failed to determine source cofiguration of kotlin plugin. Can not download core. Please verify that this or any parent project " + @@ -29,33 +35,13 @@ abstract class KotlinBasePluginWrapper: Plugin { val kotlinPluginVersion = loadKotlinVersionFromResource(log) project.getExtensions().getExtraProperties()?.set("kotlin.gradle.plugin.version", kotlinPluginVersion) - val pluginClassLoader = createPluginIsolatedClassLoader(kotlinPluginVersion, sourceBuildScript) - val plugin = getPlugin(pluginClassLoader, sourceBuildScript) + val plugin = getPlugin(this.javaClass.getClassLoader(), sourceBuildScript) plugin.apply(project) - project.getGradle().addBuildListener(FinishBuildListener(pluginClassLoader)) + project.getGradle().addBuildListener(FinishBuildListener(this.javaClass.getClassLoader(), startMemory)) } - protected abstract fun getPlugin(pluginClassLoader: ParentLastURLClassLoader, scriptHandler: ScriptHandler): Plugin - - private fun createPluginIsolatedClassLoader(projectVersion: String, sourceBuildScript: ScriptHandler): ParentLastURLClassLoader { - val dependencyHandler: DependencyHandler = sourceBuildScript.getDependencies() - val configurationsContainer: ConfigurationContainer = sourceBuildScript.getConfigurations() - - log.kotlinDebug("Creating configuration and dependency") - val kotlinPluginCoreCoordinates = "org.jetbrains.kotlin:kotlin-gradle-plugin-core:" + projectVersion - val dependency = dependencyHandler.create(kotlinPluginCoreCoordinates) - val configuration = configurationsContainer.detachedConfiguration(dependency) - - log.kotlinDebug("Resolving [" + kotlinPluginCoreCoordinates + "]") - val kotlinPluginDependencies: List = configuration.getResolvedConfiguration().getFiles({ true })!!.map { it.toURI().toURL() } - log.kotlinDebug("Resolved files: [" + kotlinPluginDependencies.toString() + "]") - log.kotlinDebug("Load plugin in parent-last URL classloader") - val kotlinPluginClassloader = ParentLastURLClassLoader(kotlinPluginDependencies, this.javaClass.getClassLoader()) - log.kotlinDebug("Class loader created") - - return kotlinPluginClassloader - } + protected abstract fun getPlugin(pluginClassLoader: ClassLoader, scriptHandler: ScriptHandler): Plugin private fun findSourceBuildScript(project: Project): ScriptHandler? { log.kotlinDebug("Looking for proper script handler") @@ -69,25 +55,24 @@ abstract class KotlinBasePluginWrapper: Plugin { return scriptHandler } log.kotlinDebug("not found, switching to parent") - curProject = curProject.getParent()!! + curProject = curProject.getParent() ?: break } return null } } open class KotlinPluginWrapper: KotlinBasePluginWrapper() { - override fun getPlugin(pluginClassLoader: ParentLastURLClassLoader, scriptHandler: ScriptHandler) = KotlinPlugin(scriptHandler, KotlinTasksProvider(pluginClassLoader)) + override fun getPlugin(pluginClassLoader: ClassLoader, scriptHandler: ScriptHandler) = KotlinPlugin(scriptHandler, KotlinTasksProvider(pluginClassLoader)) } open class KotlinAndroidPluginWrapper : KotlinBasePluginWrapper() { - override fun getPlugin(pluginClassLoader: ParentLastURLClassLoader, scriptHandler: ScriptHandler) = KotlinAndroidPlugin(scriptHandler, KotlinTasksProvider(pluginClassLoader)) + override fun getPlugin(pluginClassLoader: ClassLoader, scriptHandler: ScriptHandler) = KotlinAndroidPlugin(scriptHandler, KotlinTasksProvider(pluginClassLoader)) } open class Kotlin2JsPluginWrapper : KotlinBasePluginWrapper() { - override fun getPlugin(pluginClassLoader: ParentLastURLClassLoader, scriptHandler: ScriptHandler) = Kotlin2JsPlugin(scriptHandler, KotlinTasksProvider(pluginClassLoader)) + override fun getPlugin(pluginClassLoader: ClassLoader, scriptHandler: ScriptHandler) = Kotlin2JsPlugin(scriptHandler, KotlinTasksProvider(pluginClassLoader)) } fun Logger.kotlinDebug(message: String) { this.debug("[KOTLIN] $message") } - diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt index dd43023985e..03303d3a65d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt @@ -27,18 +27,43 @@ abstract class BaseGradleIT { deleteRecursively(workingDir) } + class BuildOptions(val withDaemon: Boolean = false) + class Project(val projectName: String, val wrapperVersion: String = "1.4", val minLogLevel: LogLevel = LogLevel.DEBUG) class CompiledProject(val project: Project, val output: String, val resultCode: Int) - fun Project.build(vararg tasks: String, check: CompiledProject.() -> Unit) { + fun Project.setupWorkingDir() { copyRecursively(File(resourcesRootFile, "testProject/$projectName"), workingDir) - val projectDir = File(workingDir, projectName) - copyDirRecursively(File(resourcesRootFile, "GradleWrapper-$wrapperVersion"), projectDir) - val cmd = createCommand(tasks) + copyDirRecursively(File(resourcesRootFile, "GradleWrapper-$wrapperVersion"), File(workingDir, projectName)) + } + + fun Project.build(vararg tasks: String, options: BuildOptions = BuildOptions(), check: CompiledProject.() -> Unit) { + val cmd = createBuildCommand(tasks, options) println("<=== Test build: ${this.projectName} $cmd ===>") + runAndCheck(cmd, check) + } + + fun stopDaemon(ver: String) { + val wrapperDir = File(resourcesRootFile, "GradleWrapper-$ver") + val cmd = createGradleCommand(arrayListOf("-stop")) + createProcess(cmd, wrapperDir) + } + + fun Project.stopDaemon(check: CompiledProject.() -> Unit) { + val cmd = createGradleCommand(arrayListOf("-stop")) + println("<=== Stop daemon: $cmd ===>") + + runAndCheck(cmd, check) + } + + private fun Project.runAndCheck(cmd: List, check: CompiledProject.() -> Unit) { + val projectDir = File(workingDir, projectName) + if (!projectDir.exists()) + setupWorkingDir() + val process = createProcess(cmd, projectDir) val (output, resultCode) = readOutput(process) @@ -94,14 +119,21 @@ abstract class BaseGradleIT { return this } - private fun Project.createCommand(params: Array): List { + private fun Project.createBuildCommand(params: Array, options: BuildOptions): List { val pathToKotlinPlugin = "-PpathToKotlinPlugin=" + File("local-repo").getAbsolutePath() val tailParameters = params.asList() + - listOf(pathToKotlinPlugin, "--no-daemon", "--${minLogLevel.name().toLowerCase()}", "-Pkotlin.gradle.test=true") + listOf( pathToKotlinPlugin, + if (options.withDaemon) "--daemon" else "--no-daemon", + "--${minLogLevel.name().toLowerCase()}", + "-Pkotlin.gradle.test=true") + return createGradleCommand(tailParameters) + } + + private fun createGradleCommand(tailParameters: List): List { return if (isWindows()) listOf("cmd", "/C", "gradlew.bat") + tailParameters - else + else listOf("/bin/bash", "./gradlew") + tailParameters } diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt index 037f19fe7db..851faa80ea9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt @@ -1,7 +1,9 @@ package org.jetbrains.kotlin.gradle -import org.junit.Test +import org.gradle.api.logging.LogLevel import org.jetbrains.kotlin.gradle.BaseGradleIT.Project +import org.junit.Ignore +import org.junit.Test class KotlinGradleIT: BaseGradleIT() { @@ -35,6 +37,34 @@ class KotlinGradleIT: BaseGradleIT() { } } + // This test isn't safe enough: gradle daemon is a singleton process (for a chosen version) and stopping it may + // affect the build environment in an unpredictable way. Therefore it is now disabled. + // TODO research the possibility to run isolated daemon build + Ignore Test fun testKotlinOnlyDaemonMemory() { + val project = Project("kotlinProject", "2.4") + + project.stopDaemon {} + + // build to "warm up" the daemon, if it is not started yet + project.build("build", options = BaseGradleIT.BuildOptions(withDaemon = true)) { + assertSuccessful() + } + + for (i in 1..3) { + project.build("build", options = BaseGradleIT.BuildOptions(withDaemon = true)) { + assertSuccessful() + val matches = "\\[PERF\\] Used memory after build: (\\d+) kb \\(([+-]?\\d+) kb\\)".toRegex().match(output) + assert(matches != null && matches.groups.size() == 3, "Used memory after build is not reported by plugin") + val reportedGrowth = matches!!.groups.get(2)!!.value.toInt() + assert(reportedGrowth <= 500, "Used memory growth $reportedGrowth > 500") + } + } + + project.stopDaemon { + assertSuccessful() + } + } + Test fun testKotlinClasspath() { Project("classpathTest", "1.6").build("build") { assertSuccessful() @@ -107,5 +137,4 @@ class KotlinGradleIT: BaseGradleIT() { assertFileExists("build/classes/main/example/AncestorClassGenerated.class") } } - } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradle/wrapper/gradle-wrapper.jar b/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000000..30d399d8d2b Binary files /dev/null and b/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradle/wrapper/gradle-wrapper.jar differ diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradle/wrapper/gradle-wrapper.properties b/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..888191f04df --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Aug 10 11:44:23 CEST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-bin.zip diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradlew b/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradlew new file mode 100755 index 00000000000..91a7e269e19 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradlew.bat b/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradlew.bat new file mode 100644 index 00000000000..8a0b282aa68 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/resources/GradleWrapper-2.4/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega