From afa8a577cc9c0da7b647fbbc0256ed00e57a5bad Mon Sep 17 00:00:00 2001 From: "konstantin.tskhovrebov" Date: Mon, 26 Sep 2022 17:42:04 +0200 Subject: [PATCH] [KT-54060] Disable bitcode embedding to frameworks for Xcode >= 14. --- .../gradle/plugin/mpp/NativeBinaryTypes.kt | 22 +++--- .../kotlin/gradle/native/GeneralNativeIT.kt | 18 ++++- .../gradle/targets/native/NativeBinaries.kt | 4 +- .../native/tasks/AdvancedCocoapodsTasks.kt | 64 +--------------- .../tasks/artifact/KotlinNativeFramework.kt | 3 +- .../kotlin/gradle/utils/ProcessUtils.kt | 73 +++++++++++++++++++ .../jetbrains/kotlin/gradle/utils/Xcode.kt | 32 ++++++++ 7 files changed, 136 insertions(+), 80 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ProcessUtils.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Xcode.kt diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaryTypes.kt b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaryTypes.kt index 9f3577aa4f4..8f56275650c 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaryTypes.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaryTypes.kt @@ -7,29 +7,25 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import org.gradle.api.Named -import org.jetbrains.kotlin.konan.target.Architecture.ARM32 -import org.jetbrains.kotlin.konan.target.Architecture.ARM64 import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.Family.* import org.jetbrains.kotlin.konan.target.KonanTarget enum class NativeBuildType( val optimized: Boolean, - val debuggable: Boolean, - private val embedBitcodeForAppleDevices: BitcodeEmbeddingMode + val debuggable: Boolean ) : Named { - RELEASE(true, false, BitcodeEmbeddingMode.BITCODE), - DEBUG(false, true, BitcodeEmbeddingMode.MARKER); + RELEASE(true, false), + DEBUG(false, true); override fun getName(): String = name.toLowerCase() - fun embedBitcode(target: KonanTarget) = with(target) { - if (family in listOf(IOS, WATCHOS, TVOS) && architecture in listOf(ARM32, ARM64)) { - embedBitcodeForAppleDevices - } else { - BitcodeEmbeddingMode.DISABLE - } - } + @Suppress("UNUSED_PARAMETER") + @Deprecated( + "Default BitcodeEmbeddingMode is BitcodeEmbeddingMode.DISABLE", + ReplaceWith("BitcodeEmbeddingMode.DISABLE") + ) + fun embedBitcode(target: KonanTarget) = BitcodeEmbeddingMode.DISABLE companion object { val DEFAULT_BUILD_TYPES = setOf(DEBUG, RELEASE) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/GeneralNativeIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/GeneralNativeIT.kt index 1d30f8f63e0..5854a2204a9 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/GeneralNativeIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/GeneralNativeIT.kt @@ -373,15 +373,29 @@ class GeneralNativeIT : BaseGradleIT() { frameworkPaths.forEach { assertFileExists(it) } assertTrue(fileInWorkingDir(headerPaths[0]).readText().contains("+ (int32_t)exported")) + val xcodeVersionString = runProcess( + listOf("/usr/bin/xcrun", "xcodebuild", "-version"), + workingDir, + System.getenv() + ).output.lines()[0].removePrefix("Xcode ") + val xcodeMajorVersion = xcodeVersionString.split(".")[0].toInt() // Check that by default release frameworks have bitcode embedded. withNativeCommandLineArguments(":linkMainReleaseFrameworkIos") { arguments -> - assertTrue("-Xembed-bitcode" in arguments) + if (xcodeMajorVersion < 14) { + assertTrue("-Xembed-bitcode" in arguments) + } else { + assertFalse("-Xembed-bitcode" in arguments) + } assertTrue("-opt" in arguments) } // Check that by default debug frameworks have bitcode marker embedded. withNativeCommandLineArguments(":linkMainDebugFrameworkIos") { arguments -> - assertTrue("-Xembed-bitcode-marker" in arguments) + if (xcodeMajorVersion < 14) { + assertTrue("-Xembed-bitcode-marker" in arguments) + } else { + assertFalse("-Xembed-bitcode-marker" in arguments) + } assertTrue("-g" in arguments) } // Check that bitcode can be disabled by setting custom compiler options diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt index cfacf67fcd4..0a185d341ad 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt @@ -19,6 +19,7 @@ import org.gradle.api.provider.Provider import org.gradle.api.tasks.AbstractExecTask import org.gradle.api.tasks.TaskProvider import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink +import org.jetbrains.kotlin.gradle.utils.Xcode import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.konan.target.KonanTarget import java.io.File @@ -278,7 +279,8 @@ class Framework( /** * Embed bitcode for the framework or not. See [BitcodeEmbeddingMode]. */ - var embedBitcode: org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode = buildType.embedBitcode(konanTarget) + var embedBitcode: org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode = + Xcode.defaultBitcodeEmbeddingMode(konanTarget, buildType) /** * Enable or disable embedding bitcode for the framework. See [BitcodeEmbeddingMode]. diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/AdvancedCocoapodsTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/AdvancedCocoapodsTasks.kt index c8b906f546c..31b26ab92e2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/AdvancedCocoapodsTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/AdvancedCocoapodsTasks.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.gradle.targets.native.cocoapods.MissingCocoapodsMess import org.jetbrains.kotlin.gradle.targets.native.cocoapods.MissingSpecReposMessage import org.jetbrains.kotlin.gradle.tasks.PodspecTask.Companion.retrievePods import org.jetbrains.kotlin.gradle.tasks.PodspecTask.Companion.retrieveSpecRepos +import org.jetbrains.kotlin.gradle.utils.runCommand import org.jetbrains.kotlin.konan.target.Family import org.jetbrains.kotlin.konan.target.HostManager import java.io.File @@ -114,69 +115,6 @@ open class PodInstallTask : CocoapodsTask() { } } -private fun runCommand( - command: List, - logger: Logger, - errorHandler: ((retCode: Int, output: String, process: Process) -> String?)? = null, - exceptionHandler: ((ex: IOException) -> Unit)? = null, - processConfiguration: ProcessBuilder.() -> Unit = { } -): String { - var process: Process? = null - try { - process = ProcessBuilder(command) - .apply { - this.processConfiguration() - }.start() - } catch (e: IOException) { - if (exceptionHandler != null) exceptionHandler(e) else throw e - } - - if (process == null) { - throw IllegalStateException("Failed to run command ${command.joinToString(" ")}") - } - - var inputText = "" - var errorText = "" - - val inputThread = thread { - inputText = process.inputStream.use { - it.reader().readText() - } - } - - val errorThread = thread { - errorText = process.errorStream.use { - it.reader().readText() - } - } - - inputThread.join() - errorThread.join() - - val retCode = process.waitFor() - logger.info( - """ - |Information about "${command.joinToString(" ")}" call: - | - |${inputText} - """.trimMargin() - ) - - check(retCode == 0) { - errorHandler?.invoke(retCode, inputText.ifBlank { errorText }, process) - ?: """ - |Executing of '${command.joinToString(" ")}' failed with code $retCode and message: - | - |$inputText - | - |$errorText - | - """.trimMargin() - } - - return inputText -} - /** * The task generates a synthetic project with all cocoapods dependencies */ diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeFramework.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeFramework.kt index a8bd879e7b0..bd9bb0dbe48 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeFramework.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeFramework.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind import org.jetbrains.kotlin.gradle.plugin.mpp.enabledOnCurrentHost import org.jetbrains.kotlin.gradle.tasks.dependsOn import org.jetbrains.kotlin.gradle.tasks.registerTask +import org.jetbrains.kotlin.gradle.utils.Xcode import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.presetName @@ -122,7 +123,7 @@ internal fun KotlinNativeArtifact.registerLinkFrameworkTask( task.linkerOptions = linkerOptions task.binaryOptions = binaryOptions task.isStaticFramework = isStatic - task.embedBitcode = embedBitcode ?: buildType.embedBitcode(target) + task.embedBitcode = embedBitcode ?: Xcode.defaultBitcodeEmbeddingMode(target, buildType) task.librariesConfiguration = librariesConfigurationName task.exportLibrariesConfiguration = exportConfigurationName task.kotlinOptions(kotlinOptionsFn) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ProcessUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ProcessUtils.kt new file mode 100644 index 00000000000..478a9e73da8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ProcessUtils.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2010-2022 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.utils + +import org.gradle.api.logging.Logger +import java.io.IOException +import kotlin.concurrent.thread + +internal fun runCommand( + command: List, + logger: Logger? = null, + errorHandler: ((retCode: Int, output: String, process: Process) -> String?)? = null, + exceptionHandler: ((ex: IOException) -> Unit)? = null, + processConfiguration: ProcessBuilder.() -> Unit = { } +): String { + var process: Process? = null + try { + process = ProcessBuilder(command) + .apply { + this.processConfiguration() + }.start() + } catch (e: IOException) { + if (exceptionHandler != null) exceptionHandler(e) else throw e + } + + if (process == null) { + throw IllegalStateException("Failed to run command ${command.joinToString(" ")}") + } + + var inputText = "" + var errorText = "" + + val inputThread = thread { + inputText = process.inputStream.use { + it.reader().readText() + } + } + + val errorThread = thread { + errorText = process.errorStream.use { + it.reader().readText() + } + } + + inputThread.join() + errorThread.join() + + val retCode = process.waitFor() + logger?.info( + """ + |Information about "${command.joinToString(" ")}" call: + | + |${inputText} + """.trimMargin() + ) + + check(retCode == 0) { + errorHandler?.invoke(retCode, inputText.ifBlank { errorText }, process) + ?: """ + |Executing of '${command.joinToString(" ")}' failed with code $retCode and message: + | + |$inputText + | + |$errorText + | + """.trimMargin() + } + + return inputText +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Xcode.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Xcode.kt new file mode 100644 index 00000000000..ee4dd97e76d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Xcode.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2022 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.utils + +import org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode +import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType +import org.jetbrains.kotlin.konan.target.Architecture.ARM32 +import org.jetbrains.kotlin.konan.target.Architecture.ARM64 +import org.jetbrains.kotlin.konan.target.Family.* +import org.jetbrains.kotlin.konan.target.KonanTarget + +internal object Xcode { + val currentVersion: String by lazy { + val out = runCommand(listOf("/usr/bin/xcrun", "xcodebuild", "-version")) + out.lines()[0].removePrefix("Xcode ") + } + + fun defaultBitcodeEmbeddingMode(target: KonanTarget, buildType: NativeBuildType): BitcodeEmbeddingMode { + if (currentVersion.split(".")[0].toInt() < 14) { + if (target.family in listOf(IOS, WATCHOS, TVOS) && target.architecture in listOf(ARM32, ARM64)) { + when (buildType) { + NativeBuildType.RELEASE -> return BitcodeEmbeddingMode.BITCODE + NativeBuildType.DEBUG -> return BitcodeEmbeddingMode.MARKER + } + } + } + return BitcodeEmbeddingMode.DISABLE + } +} \ No newline at end of file