diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/GeneralNativeIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/GeneralNativeIT.kt new file mode 100644 index 00000000000..d196b25d5da --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/GeneralNativeIT.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle + +import org.jetbrains.kotlin.gradle.util.modify +import org.junit.Test + +class GeneralNativeIT : BaseGradleIT() { + + @Test + fun testParallelExecutionDetection(): Unit = with(transformProjectWithPluginsDsl("native-parallel")) { + val compileTasks = arrayOf(":one:compileKotlinLinux", ":two:compileKotlinLinux") + // Check that parallel in-process execution fails with the corresponding message. + build(*compileTasks) { + assertFailed() + assertContains("Parallel in-process execution of the Kotlin/Native compiler detected.") + } + + // Parallel execution without daemon => ok. + build("clean", *compileTasks, "-Pkotlin.native.disableCompilerDaemon=true") { + assertSuccessful() + assertTasksExecuted(*compileTasks) + } + + // org.gradle.parallel must be set in the properties file (not in command line). + projectDir.resolve("gradle.properties").modify { + it.replace("org.gradle.parallel=true", "org.gradle.parallel=false") + } + + // Sequential execution => ok. + build("clean", *compileTasks) { + assertSuccessful() + assertTasksExecuted(*compileTasks) + } + + // Check for KT-37696. + // Add an incorrect code to trigger compilation failure. + projectDir.resolve("one/src/linuxMain/kotlin/main.kt").appendText("\nfun incorrect") + gradleBuildScript("two").appendText(""" + + val compileKotlinLinux by tasks.getting + compileKotlinLinux.mustRunAfter(":one:compileKotlinLinux") + """.trimIndent()) + + build("clean", *compileTasks, "--continue") { + assertFailed() + assertTasksFailed(":one:compileKotlinLinux") + assertTasksExecuted(":two:compileKotlinLinux") + assertNotContains("Parallel in-process execution of the Kotlin/Native compiler detected.") + } + + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/build.gradle.kts new file mode 100644 index 00000000000..8761a3919b5 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("org.jetbrains.kotlin.multiplatform").version("").apply(false) +} + +allprojects { + repositories { + mavenLocal() + jcenter() + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/gradle.properties new file mode 100644 index 00000000000..af82e006a37 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/gradle.properties @@ -0,0 +1 @@ +org.gradle.parallel=true \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/one/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/one/build.gradle.kts new file mode 100644 index 00000000000..2e8717ef308 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/one/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("org.jetbrains.kotlin.multiplatform") +} + +kotlin { + linuxX64("linux") + + sourceSets["commonMain"].dependencies { + implementation(kotlin("stdlib")) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/one/src/linuxMain/kotlin/main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/one/src/linuxMain/kotlin/main.kt new file mode 100644 index 00000000000..8dcc2c45cb6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/one/src/linuxMain/kotlin/main.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +fun main() { + println(42) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/settings.gradle.kts new file mode 100644 index 00000000000..540c7457e1a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/settings.gradle.kts @@ -0,0 +1,9 @@ +pluginManagement { + repositories { + mavenLocal() + jcenter() + gradlePluginPortal() + } +} + +include(":one", ":two") \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/two/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/two/build.gradle.kts new file mode 100644 index 00000000000..2e8717ef308 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/two/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("org.jetbrains.kotlin.multiplatform") +} + +kotlin { + linuxX64("linux") + + sourceSets["commonMain"].dependencies { + implementation(kotlin("stdlib")) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/two/src/linuxMain/kotlin/main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/two/src/linuxMain/kotlin/main.kt new file mode 100644 index 00000000000..8dcc2c45cb6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-parallel/two/src/linuxMain/kotlin/main.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +fun main() { + println(42) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt index 7f13f3086df..a35426d03bc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt @@ -90,7 +90,8 @@ internal abstract class KotlinToolRunner( } } - private fun runInProcess(args: List) { + // TODO: Make it private again once KT-37550 is fixed. + protected open fun runInProcess(args: List) { val oldProperties = setUpSystemProperties() try { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt index 38aa50a119e..d2dc44c6857 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt @@ -78,6 +78,50 @@ internal abstract class KotlinNativeToolRunner( override fun transformArgs(args: List) = listOf(toolName) + args final override fun getCustomJvmArgs() = project.jvmArgs + + final override fun runInProcess(args: List) { + withParallelExecutionGuard { + super.runInProcess(args) + } + } + + // TODO: Remove once KT-37550 is fixed + private inline fun withParallelExecutionGuard(action: () -> Unit) { + try { + if (PropertiesProvider(project).nativeEnableParallelExecutionCheck) { + System.getProperties().compute(PARALLEL_EXECUTION_GUARD_PROPERTY) { _, value -> + check(value == null) { PARALLEL_EXECUTION_ERROR_MESSAGE } + "true" + } + } + + action() + + } finally { + if (PropertiesProvider(project).nativeEnableParallelExecutionCheck) { + System.clearProperty(PARALLEL_EXECUTION_GUARD_PROPERTY) + } + } + } + + companion object { + private const val PARALLEL_EXECUTION_GUARD_PROPERTY = "org.jetbrains.kotlin.native.compiler.running" + + private val PARALLEL_EXECUTION_ERROR_MESSAGE = """ + Parallel in-process execution of the Kotlin/Native compiler detected. + + At this moment the parallel execution of several compiler instances in the same process is not supported. + To fix this, you can do one of the following things: + + - Disable in-process execution. To do this, set '${PropertiesProvider.KOTLIN_NATIVE_DISABLE_COMPILER_DAEMON}=true' project property. + + - Disable parallel task execution. To do this, set 'org.gradle.parallel=false' project property. + + If you still want to run the compiler in-process in parallel, you may disable this check by setting project + property '${PropertiesProvider.KOTLIN_NATIVE_ENABLE_PARALLEL_EXECUTION_CHECK}=false'. Note that in this case the compiler may fail. + + """.trimIndent() + } } /** A common ancestor for all runners that run the cinterop tool. */ diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt index aa7800d314e..acb5eaa7319 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt @@ -189,7 +189,11 @@ internal class PropertiesProvider private constructor(private val project: Proje * Forces to run a compilation in a separate JVM. */ val nativeDisableCompilerDaemon: Boolean? - get() = booleanProperty("kotlin.native.disableCompilerDaemon") + get() = booleanProperty(KOTLIN_NATIVE_DISABLE_COMPILER_DAEMON) + + // TODO: Remove once KT-37550 is fixed + val nativeEnableParallelExecutionCheck: Boolean + get() = booleanProperty(KOTLIN_NATIVE_ENABLE_PARALLEL_EXECUTION_CHECK) ?: true /** * Dependencies caching strategy. The default is static. @@ -238,6 +242,11 @@ internal class PropertiesProvider private constructor(private val project: Proje private const val CACHED_PROVIDER_EXT_NAME = "kotlin.properties.provider" + internal const val KOTLIN_NATIVE_DISABLE_COMPILER_DAEMON = "kotlin.native.disableCompilerDaemon" + + // TODO: Remove once KT-37550 is fixed + internal const val KOTLIN_NATIVE_ENABLE_PARALLEL_EXECUTION_CHECK = "kotlin.native.enableParallelExecutionCheck" + operator fun invoke(project: Project): PropertiesProvider = with(project.extensions.extraProperties) { if (!has(CACHED_PROVIDER_EXT_NAME)) {