diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CommonNativeIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CommonNativeIT.kt index 40277b0fb54..85a9758c41b 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CommonNativeIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CommonNativeIT.kt @@ -1,101 +1,93 @@ /* - * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2023 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.native -import org.jetbrains.kotlin.gradle.BaseGradleIT -import org.jetbrains.kotlin.gradle.GradleVersionRequired -import org.jetbrains.kotlin.gradle.transformProjectWithPluginsDsl -import org.jetbrains.kotlin.konan.target.HostManager -import org.junit.Assume -import org.junit.BeforeClass -import org.junit.Test +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.gradle.util.capitalize +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.condition.OS import java.util.* -class CommonNativeIT : BaseGradleIT() { +@OsCondition(supportedOn = [OS.MAC], enabledOnCI = [OS.MAC]) +@DisplayName("K/N tests of apple native common libs") +@NativeGradlePluginTests +class CommonNativeIT : KGPBaseTest() { - override val defaultGradleVersion: GradleVersionRequired - get() = GradleVersionRequired.FOR_MPP_SUPPORT + // TODO(Dmitrii Krasnov): remove it, when KT-58104 will be fixed + override val defaultBuildOptions = super.defaultBuildOptions.copy(statisticsForceValidation = false) + + private val String.withPrefix get() = "native-apple-devices-common/$this" + + @DisplayName("Common ios") + @GradleTest + fun testCommonIos(gradleVersion: GradleVersion) { + doCommonNativeTest( + "common-ios", + libTargets = listOf("iosLibArm64", "iosLibX64"), + appTargets = listOf("iosArm64", "iosX64"), + gradleVersion + ) + } + + @DisplayName("Common watchos") + @GradleTest + fun testCommonWatchos(gradleVersion: GradleVersion) { + doCommonNativeTest( + "common-watchos", + libTargets = listOf("watchosLibArm32", "watchosLibArm64", "watchosLibX64"), + appTargets = listOf("watchosArm32", "watchosArm64", "watchosX64"), + gradleVersion + ) + } + + @DisplayName("Common tvos") + @GradleTest + fun testCommonTvos(gradleVersion: GradleVersion) { + doCommonNativeTest( + "common-tvos", + libTargets = listOf("tvosLibArm64", "tvosLibX64"), + appTargets = listOf("tvosArm64", "tvosX64"), + gradleVersion + ) + } private fun doCommonNativeTest( projectName: String, libTargets: List, - appTargets: List - ) = with(transformProjectWithPluginsDsl(projectName, directoryPrefix = "native-apple-devices-common")) { - gradleProperties().apply { + appTargets: List, + gradleVersion: GradleVersion + ) { + nativeProject(projectName.withPrefix, gradleVersion) { + configureJvmMemory() - } - val libCompileTasks = libTargets.map { ":lib:compileKotlin${it.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}" } - val appCompileTasks = appTargets.map { ":app:compileKotlin${it.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}" } - val appLinkFrameworkTasks = appTargets.map { - ":app:linkDebugFramework${ - it.replaceFirstChar { - if (it.isLowerCase()) it.titlecase( - Locale.getDefault() - ) else it.toString() + val libCompileTasks = libTargets.map { ":lib:compileKotlin${it.capitalize()}" } + val appCompileTasks = appTargets.map { ":app:compileKotlin${it.capitalize()}" } + val appLinkFrameworkTasks = appTargets.map { ":app:linkDebugFramework${it.capitalize()}" } + val appLinkTestTasks = appTargets.map { ":app:linkDebugTest${it.capitalize()}" } + build(":lib:publish") { + assertTasksExecuted(libCompileTasks) + libTargets.forEach { + assertOutputContains("Configuring $it") + assertFileInProjectExists("lib/build/classes/kotlin/$it/main/klib/lib.klib") } - }" } - val appLinkTestTasks = appTargets.map { ":app:linkDebugTest${it.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}" } - - build(":lib:publish") { - assertSuccessful() - assertTasksExecuted(libCompileTasks) - libTargets.forEach { - assertContains("Configuring $it") - assertFileExists("lib/build/classes/kotlin/$it/main/klib/lib.klib") } - } - build(":app:build", *appLinkTestTasks.toTypedArray()) { - assertSuccessful() - assertTasksExecuted(appCompileTasks) - assertTasksExecuted(appLinkFrameworkTasks) - assertTasksExecuted(appLinkTestTasks) + build(":app:build", *appLinkTestTasks.toTypedArray()) { + assertTasksExecuted(appCompileTasks) + assertTasksExecuted(appLinkFrameworkTasks) + assertTasksExecuted(appLinkTestTasks) - appTargets.forEach { - assertFileExists("app/build/classes/kotlin/$it/main/klib/app.klib") - assertFileExists("app/build/bin/$it/debugFramework") - assertFileExists("app/build/bin/$it/debugTest") + appTargets.forEach { + assertFileInProjectExists("app/build/classes/kotlin/$it/main/klib/app.klib") + assertDirectoryInProjectExists("app/build/bin/$it/debugFramework") + assertDirectoryInProjectExists("app/build/bin/$it/debugTest") + } } } } - - @Test - fun testCommonIos() { - doCommonNativeTest( - "common-ios", - libTargets = listOf("iosLibArm64", "iosLibX64"), - appTargets = listOf("iosArm64", "iosX64") - ) - } - - @Test - fun testCommonWatchos() { - doCommonNativeTest( - "common-watchos", - libTargets = listOf("watchosLibArm32", "watchosLibArm64", "watchosLibX64"), - appTargets = listOf("watchosArm32", "watchosArm64", "watchosX64") - ) - } - - @Test - fun testCommonTvos() { - doCommonNativeTest( - "common-tvos", - libTargets = listOf("tvosLibArm64", "tvosLibX64"), - appTargets = listOf("tvosArm64", "tvosX64") - ) - } - - companion object { - @BeforeClass - @JvmStatic - fun assumeItsMac() { - Assume.assumeTrue(HostManager.hostIsMac) - } - } -} - +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/FatFrameworkIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/FatFrameworkIT.kt index 5bb0c9fe4e4..d93ba33b65d 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/FatFrameworkIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/FatFrameworkIT.kt @@ -47,7 +47,7 @@ class FatFrameworkIT : KGPBaseTest() { nativeProject( "native-fat-framework/smoke", gradleVersion, - // TODO(d.krasnov): return default statisticsForceValidation, when KT-58104 will be fixed + // TODO(Dmitrii Krasnov): return default statisticsForceValidation, when KT-58104 will be fixed buildOptions = defaultBuildOptions.copy(statisticsForceValidation = false) ) { buildGradleKts.modify { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt index 6229bad1b2b..ffd7b9c0203 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.gradle.util.modify import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.presetName import org.jetbrains.kotlin.test.util.KtTestUtil +import org.jetbrains.kotlin.utils.addToStdlib.ifTrue import java.io.File import java.nio.file.Files import java.nio.file.Path @@ -56,7 +57,6 @@ fun KGPBaseTest.project( projectPath.enableCacheRedirector() projectPath.enableAndroidSdk() - if (addHeapDumpOptions) projectPath.addHeapDumpOptions() val gradleRunner = GradleRunner .create() @@ -74,6 +74,7 @@ fun KGPBaseTest.project( enableBuildScan = enableBuildScan, enableGradleDebug = enableGradleDebug, ) + addHeapDumpOptions.ifTrue { testProject.addHeapDumpOptions() } localRepoDir?.let { testProject.configureLocalRepository(localRepoDir) } if (buildJdk != null) testProject.setupNonDefaultJdk(buildJdk) testProject.addKotlinCompilerArgumentsPlugin() @@ -595,49 +596,14 @@ internal fun Path.enableCacheRedirector() { } } -private fun Path.addHeapDumpOptions() { - val propertiesFile = resolve("gradle.properties") - if (!propertiesFile.exists()) propertiesFile.createFile() - - val propertiesContent = propertiesFile.readText() - val (existingJvmArgsLine, otherLines) = propertiesContent - .lines() - .partition { - it.trim().startsWith("org.gradle.jvmargs") - } - - val heapDumpOutOfErrorStr = "-XX:+HeapDumpOnOutOfMemoryError" - val heapDumpPathStr = "-XX:HeapDumpPath=\"${System.getProperty("user.dir")}${File.separatorChar}build\"" - - if (existingJvmArgsLine.isEmpty()) { - propertiesFile.writeText( - """ - |# modified in addHeapDumpOptions - |org.gradle.jvmargs=$heapDumpOutOfErrorStr $heapDumpPathStr - | - |$propertiesContent - """.trimMargin() - ) - } else { - val argsLine = existingJvmArgsLine.first() - val appendedOptions = buildString { - if (!argsLine.contains("HeapDumpOnOutOfMemoryError")) append(" $heapDumpOutOfErrorStr") - if (!argsLine.contains("HeapDumpPath")) append(" $heapDumpPathStr") - } - - if (appendedOptions.isNotEmpty()) { - propertiesFile.writeText( - """ - # modified in addHeapDumpOptions - $argsLine$appendedOptions - - ${otherLines.joinToString(separator = "\n")} - """.trimIndent() - ) - } else { - println("<=== Heap dump options are already exists! ===>") - } - } +private fun GradleProject.addHeapDumpOptions() { + addPropertyToGradleProperties( + propertyName = "org.gradle.jvmargs", + mapOf( + "-XX:+HeapDumpOnOutOfMemoryError" to "-XX:+HeapDumpOnOutOfMemoryError", + "-XX:HeapDumpPath" to "-XX:HeapDumpPath=\"${System.getProperty("user.dir")}${File.separatorChar}build\"" + ), + ) } private const val SINGLE_NATIVE_TARGET_PLACEHOLDER = "" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testHelpers.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testHelpers.kt index 20c2e8b58ef..441dca0f407 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testHelpers.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testHelpers.kt @@ -79,3 +79,81 @@ fun TestProject.makeSnapshotTo(destinationPath: String) { copyTo(dest.resolve(fileName)) } } + +/** + * + * Configures the JVM memory settings for the Gradle project. + * @param memorySizeInGb The amount of memory to allocate to the JVM, in gigabytes. + * Defaults to 1 gigabyte. + */ +fun GradleProject.configureJvmMemory(memorySizeInGb: Number = 1) { + addPropertyToGradleProperties( + propertyName = "org.gradle.jvmargs", + mapOf("-Xmx" to "-Xmx${memorySizeInGb}g") + ) +} + +/** + * Adds the given options to a Gradle property specified by name, in the project's Gradle properties file. + * If the property does not exist, it is created. + * @param propertyName The name of the Gradle property to modify or create. + * @param propertyValues Map with key = "option prefix", and value = "option value". + * For example, for options: -Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError + * Map would be look like: Map.of("-Xmx", "-Xmx2g", + * "-XX:MaxMetaspaceSize","-XX:MaxMetaspaceSize=512m", + * "-XX:+HeapDumpOnOutOfMemoryError", "-XX:+HeapDumpOnOutOfMemoryError" ) + */ +fun GradleProject.addPropertyToGradleProperties( + propertyName: String, + propertyValues: Map +) { + if (!gradleProperties.exists()) gradleProperties.createFile() + + val propertiesContent = gradleProperties.readText() + val (existingPropertyLine, otherLines) = propertiesContent + .lines() + .partition { + it.trim().startsWith(propertyName) + } + + if (existingPropertyLine.isEmpty()) { + gradleProperties.writeText( + """ + |${propertyName}=${propertyValues.values.joinToString(" ")} + | + |$propertiesContent + """.trimMargin() + ) + } else { + val argsLine = existingPropertyLine.single() + val optionsToRewrite = mutableListOf() + val appendedOptions = buildString { + propertyValues.forEach { + if (argsLine.contains(it.key) && + !argsLine.contains(it.value) + ) optionsToRewrite.add(it.value) + else + if (!argsLine.contains(it.key)) append(" ${it.value}") + } + } + + assert(optionsToRewrite.isEmpty()) { + """ + |You are trying to write options: $optionsToRewrite + |for property: $propertyName + |in $gradleProperties + |But these options are already exists with another values. + |Current property value is: $argsLine + """.trimMargin() + } + + gradleProperties.writeText( + """ + |$argsLine$appendedOptions + | + |${otherLines.joinToString(separator = "\n")} + """.trimMargin() + ) + + } +} \ No newline at end of file