diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AppleFrameworkIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AppleFrameworkIT.kt index a9f790c5106..e25ce2054c5 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AppleFrameworkIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AppleFrameworkIT.kt @@ -34,7 +34,9 @@ class AppleFrameworkIT : BaseGradleIT() { customEnvironmentVariables = mapOf( "CONFIGURATION" to "debug", "SDK_NAME" to "iphoneos123", - "ARCHS" to "arm64" + "ARCHS" to "arm64", + "TARGET_BUILD_DIR" to "no use", + "FRAMEWORKS_FOLDER_PATH" to "no use" ) ) build("assembleDebugAppleFrameworkForXcodeIosArm64", options = options) { @@ -60,7 +62,9 @@ class AppleFrameworkIT : BaseGradleIT() { customEnvironmentVariables = mapOf( "CONFIGURATION" to "Release", "SDK_NAME" to "iphonesimulator", - "ARCHS" to "arm64 x86_64" + "ARCHS" to "arm64 x86_64", + "TARGET_BUILD_DIR" to "no use", + "FRAMEWORKS_FOLDER_PATH" to "no use" ) ) build("assembleReleaseAppleFrameworkForXcode", options = options) { @@ -75,14 +79,11 @@ class AppleFrameworkIT : BaseGradleIT() { } @Test - fun `check there aren't Xcode tasks without Xcode environment`() { + fun `check embedAndSignAppleFrameworkForXcode fail`() { with(Project("sharedAppleFramework")) { - build("tasks") { - assertSuccessful() - assertTasksNotRegistered( - ":shared:assembleDebugAppleFrameworkForXcodeIosArm64", - ":shared:embedAndSignAppleFrameworkForXcode" - ) + build(":shared:embedAndSignAppleFrameworkForXcode") { + assertTasksFailed(":shared:embedAndSignAppleFrameworkForXcode") + assertContains("Please run the embedAndSignAppleFrameworkForXcode task from Xcode") } } } @@ -106,9 +107,7 @@ class AppleFrameworkIT : BaseGradleIT() { ":shared:assembleDebugAppleFrameworkForXcodeIosArm64", ":shared:embedAndSignAppleFrameworkForXcode", ":shared:assembleCustomDebugAppleFrameworkForXcodeIosArm64", - ":shared:embedAndSignCustomAppleFrameworkForXcode" - ) - assertTasksNotRegistered( + ":shared:embedAndSignCustomAppleFrameworkForXcode", ":shared:assembleDebugAppleFrameworkForXcodeIosX64", ":shared:assembleReleaseAppleFrameworkForXcodeIosX64", ":shared:assembleReleaseAppleFrameworkForXcodeIosArm64", @@ -121,7 +120,7 @@ class AppleFrameworkIT : BaseGradleIT() { } @Test - fun `check there isn't embedAndSignAppleFrameworkForXcode task without required Xcode environments`() { + fun `check embedAndSignAppleFrameworkForXcode was registered without required Xcode environments`() { with(Project("sharedAppleFramework")) { val options: BuildOptions = defaultBuildOptions().copy( customEnvironmentVariables = mapOf( @@ -133,16 +132,24 @@ class AppleFrameworkIT : BaseGradleIT() { build("tasks", options = options) { assertSuccessful() assertTasksRegistered( - ":shared:assembleDebugAppleFrameworkForXcodeIosArm64", - ":shared:assembleCustomDebugAppleFrameworkForXcodeIosArm64" + ":shared:embedAndSignAppleFrameworkForXcode", + ":shared:embedAndSignCustomAppleFrameworkForXcode" ) assertTasksNotRegistered( - ":shared:embedAndSignAppleFrameworkForXcode", + ":shared:assembleReleaseAppleFrameworkForXcodeIosX64", ":shared:assembleDebugAppleFrameworkForXcodeIosX64", - ":shared:embedAndSignCustomAppleFrameworkForXcode", - ":shared:assembleCustomDebugAppleFrameworkForXcodeIosX64" + ":shared:assembleReleaseAppleFrameworkForXcodeIosArm64", + ":shared:assembleDebugAppleFrameworkForXcodeIosArm64", + ":shared:assembleCustomDebugAppleFrameworkForXcodeIosX64", + ":shared:assembleCustomReleaseAppleFrameworkForXcodeIosX64", + ":shared:assembleCustomDebugAppleFrameworkForXcodeIosArm64", + ":shared:assembleCustomReleaseAppleFrameworkForXcodeIosArm64" ) } + build(":shared:embedAndSignCustomAppleFrameworkForXcode", options = options) { + assertTasksFailed(":shared:embedAndSignCustomAppleFrameworkForXcode") + assertContains("Please run the embedAndSignCustomAppleFrameworkForXcode task from Xcode") + } } } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt index 6b71b568154..75686751ea3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.apple +import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.plugins.BasePlugin @@ -20,7 +21,6 @@ import org.jetbrains.kotlin.gradle.tasks.locateTask import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.konan.target.KonanTarget -import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly import java.io.File private object XcodeEnvironment { @@ -61,6 +61,15 @@ private object XcodeEnvironment { } val sign: String? get() = System.getenv("EXPANDED_CODE_SIGN_IDENTITY") + + override fun toString() = """ + XcodeEnvironment: + buildType=$buildType + targets=$targets + frameworkSearchDir=$frameworkSearchDir + embeddedFrameworksDir=$embeddedFrameworksDir + sign=$sign + """.trimIndent() } private fun Project.registerAssembleAppleFrameworkTask(framework: Framework): TaskProvider? { @@ -90,29 +99,27 @@ private fun Project.registerAssembleAppleFrameworkTask(framework: Framework): Ta "Specify 'KOTLIN_FRAMEWORK_BUILD_TYPE' to 'debug' or 'release'" ) } else { - logger.debug( - "Not registering $frameworkTaskName, since not called from Xcode " + - "('SDK_NAME', 'ARCHS' and 'CONFIGURATION' not provided)" - ) + logger.debug("Not registering $frameworkTaskName, since not called from Xcode") } return null } - if (frameworkBuildType != envBuildType || !envTargets.contains(frameworkTarget.konanTarget)) return null - - return if (needFatFramework) { - locateOrRegisterTask(frameworkTaskName) { task -> - task.group = BasePlugin.BUILD_GROUP + return when { + !envTargets.contains(frameworkTarget.konanTarget) -> locateOrRegisterTask(frameworkTaskName) { task -> + task.description = "Packs $frameworkBuildType ${frameworkTarget.name} framework for Xcode" + task.isEnabled = false + } + needFatFramework -> locateOrRegisterTask(frameworkTaskName) { task -> task.description = "Packs $frameworkBuildType fat framework for Xcode" task.baseName = framework.baseName task.destinationDir = appleFrameworkDir(envFrameworkSearchDir) + task.isEnabled = frameworkBuildType == envBuildType }.also { it.configure { task -> task.from(framework) } } - } else { - registerTask(frameworkTaskName) { task -> - task.group = BasePlugin.BUILD_GROUP + else -> registerTask(frameworkTaskName) { task -> task.description = "Packs $frameworkBuildType ${frameworkTarget.name} framework for Xcode" + task.isEnabled = frameworkBuildType == envBuildType task.dependsOn(framework.linkTaskName) task.from(framework.outputDirectory) task.into(appleFrameworkDir(envFrameworkSearchDir)) @@ -121,8 +128,6 @@ private fun Project.registerAssembleAppleFrameworkTask(framework: Framework): Ta } internal fun Project.registerEmbedAndSignAppleFrameworkTask(framework: Framework) { - val assembleTask = registerAssembleAppleFrameworkTask(framework) ?: return - val envBuildType = XcodeEnvironment.buildType val envTargets = XcodeEnvironment.targets val envEmbeddedFrameworksDir = XcodeEnvironment.embeddedFrameworksDir @@ -132,32 +137,46 @@ internal fun Project.registerEmbedAndSignAppleFrameworkTask(framework: Framework val frameworkTaskName = lowerCamelCaseName("embedAndSign", framework.namePrefix, "AppleFrameworkForXcode") if (envBuildType == null || envTargets.isEmpty() || envEmbeddedFrameworksDir == null || envFrameworkSearchDir == null) { - logger.debug( - "Not registering $frameworkTaskName, since not called from Xcode " + - "('SDK_NAME', 'CONFIGURATION', 'TARGET_BUILD_DIR', 'ARCHS' and 'FRAMEWORKS_FOLDER_PATH' not provided)" - ) + locateOrRegisterTask(frameworkTaskName) { task -> + task.group = BasePlugin.BUILD_GROUP + task.description = "Embed and sign ${framework.namePrefix} framework as requested by Xcode's environment variables" + task.doFirst { + val envConfiguration = System.getenv("CONFIGURATION") + if (envConfiguration != null && envBuildType == null) { + throw IllegalStateException( + "Unable to detect Kotlin framework build type for CONFIGURATION=$envConfiguration automatically. " + + "Specify 'KOTLIN_FRAMEWORK_BUILD_TYPE' to 'debug' or 'release'" + + "\n$XcodeEnvironment" + ) + } else { + throw IllegalStateException( + "Please run the $frameworkTaskName task from Xcode " + + "('SDK_NAME', 'CONFIGURATION', 'TARGET_BUILD_DIR', 'ARCHS' and 'FRAMEWORKS_FOLDER_PATH' not provided)" + + "\n$XcodeEnvironment" + ) + } + } + } return } - if (framework.buildType != envBuildType || !envTargets.contains(framework.konanTarget)) return - if (locateTask(frameworkTaskName) != null) return - - registerTask(frameworkTaskName) { task -> - task.group = "build" + val embedAndSignTask = locateOrRegisterTask(frameworkTaskName) { task -> + task.group = BasePlugin.BUILD_GROUP task.description = "Embed and sign ${framework.namePrefix} framework as requested by Xcode's environment variables" - - task.dependsOn(assembleTask) task.inputs.apply { property("type", envBuildType) property("targets", envTargets) property("embeddedFrameworksDir", envEmbeddedFrameworksDir) property("sign", envSign) } + } - task.from(appleFrameworkDir(envFrameworkSearchDir)) { spec -> - spec.include(framework.outputFile.name + "/**") - } + val assembleTask = registerAssembleAppleFrameworkTask(framework) ?: return + if (framework.buildType != envBuildType || !envTargets.contains(framework.konanTarget)) return + embedAndSignTask.configure { task -> + task.dependsOn(assembleTask) + task.from(appleFrameworkDir(envFrameworkSearchDir)) { it.include(framework.outputFile.name + "/**") } task.into(envEmbeddedFrameworksDir) if (envSign != null) {