From ca0b0e61e8311a0dd1d28a6f0eae94630a88e2eb Mon Sep 17 00:00:00 2001 From: Artem Daugel-Dauge Date: Tue, 16 May 2023 14:57:48 +0200 Subject: [PATCH] [Gradle] Add workarounds in CocoaPods plugin for compatibility with Xcode 14.3 Raise deployment target for 3rd-party pods if it's less then minimal supported by the new Xcode ^KT-57539 Verification Pending --- .../kotlin/gradle/native/CocoaPodsIT.kt | 2 +- .../kotlin/gradle/native/GeneralNativeIT.kt | 2 +- .../native/NativeDownloadAndPlatformLibsIT.kt | 10 +-- .../kotlin-library/build.gradle.kts | 2 + .../native-cocoapods-single/ios-app/Podfile | 2 +- .../kotlin-library/build.gradle.kts | 2 + .../pod_dependency/pod_dependency.podspec | 2 + .../subspec_dependency.podspec | 2 + .../native/cocoapods/KotlinCocoapodsPlugin.kt | 1 + .../native/cocoapods/tasks/PodGenTask.kt | 73 ++++++++++++++----- .../jetbrains/kotlin/gradle/utils/Xcode.kt | 27 ++++++- .../kotlin/gradle/utils/XcodeVersionTest.kt | 31 ++++++++ 12 files changed, 125 insertions(+), 31 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/utils/XcodeVersionTest.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt index acaf367b460..044c24306a2 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.gradle.native -import org.gradle.api.logging.configuration.WarningMode import org.jetbrains.kotlin.gradle.* import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.DUMMY_FRAMEWORK_TASK_NAME import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_BUILD_TASK_NAME @@ -1742,6 +1741,7 @@ class CocoaPodsIT : BaseGradleIT() { spec.summary = 'CocoaPods test library' spec.vendored_frameworks = 'build/cocoapods/framework/${frameworkName ?: "kotlin_library"}.framework' spec.libraries = 'c++' + spec.ios.deployment_target = '11.0' spec.dependency 'pod_dependency', '1.0' spec.dependency 'subspec_dependency/Core', '1.0' spec.pod_target_xcconfig = { 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 67149bacb36..ad1477841ec 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 @@ -364,7 +364,7 @@ class GeneralNativeIT : BaseGradleIT() { frameworkPaths.forEach { assertFileExists(it) } assertTrue(fileInWorkingDir(headerPaths[0]).readText().contains("+ (int32_t)exported")) - val xcodeMajorVersion = Xcode!!.currentVersion.split(".")[0].toInt() + val xcodeMajorVersion = Xcode!!.currentVersion.major // Check that by default release frameworks have bitcode embedded. withNativeCommandLineArguments(":linkMainReleaseFrameworkIos") { arguments -> diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeDownloadAndPlatformLibsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeDownloadAndPlatformLibsIT.kt index 861fb07dd56..6f6559ab4d2 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeDownloadAndPlatformLibsIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeDownloadAndPlatformLibsIT.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.gradle.transformProjectWithPluginsDsl import org.jetbrains.kotlin.gradle.util.modify import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader import org.jetbrains.kotlin.gradle.utils.Xcode +import org.jetbrains.kotlin.gradle.utils.XcodeVersion import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.presetName @@ -252,15 +253,8 @@ class NativeDownloadAndPlatformLibsIT : BaseGradleIT() { Assume.assumeTrue(maven != MAVEN_CENTRAL) if (HostManager.hostIsMac) { - val xcodeVersion = Xcode!!.currentVersion - val versionSplit = xcodeVersion.split("(\\s+|\\.|-)".toRegex()) - check(versionSplit.size >= 2) { - "Unrecognised version of Xcode $xcodeVersion was split to $versionSplit" - } - val major = versionSplit[0].toInt() - val minor = versionSplit[1].toInt() // Building platform libs require Xcode 14.1 - Assume.assumeTrue(major >= 14 && minor >= 1) + Assume.assumeTrue(Xcode!!.currentVersion >= XcodeVersion(14, 1)) } with(transformNativeTestProjectWithPluginDsl("native-download-maven")) { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-multiple/kotlin-library/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-multiple/kotlin-library/build.gradle.kts index 77d1ac4526a..6dfd43a2b32 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-multiple/kotlin-library/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-multiple/kotlin-library/build.gradle.kts @@ -22,5 +22,7 @@ kotlin { homepage = "https://github.com/JetBrains/kotlin" pod("pod_dependency", "1.0", project.file("../pod_dependency")) pod("subspec_dependency/Core", "1.0", project.file("../subspec_dependency")) + + ios.deploymentTarget = "11.0" } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/ios-app/Podfile b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/ios-app/Podfile index 9bde3b530da..70db378041a 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/ios-app/Podfile +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/ios-app/Podfile @@ -7,4 +7,4 @@ target 'ios-app' do pod 'subspec_dependency', :path => '../subspec_dependency' pod 'kotlin_library', :path => '../kotlin-library' #import_pod_directive -end \ No newline at end of file +end diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/kotlin-library/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/kotlin-library/build.gradle.kts index 8e421d60b51..6db74ab0c57 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/kotlin-library/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/kotlin-library/build.gradle.kts @@ -24,5 +24,7 @@ kotlin { pod("pod_dependency", "1.0", project.file("../pod_dependency/pod_dependency.podspec")) pod("subspec_dependency/Core", "1.0", project.file("../subspec_dependency")) podfile = project.file("../ios-app/Podfile") + + ios.deploymentTarget = "11.0" } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/pod_dependency/pod_dependency.podspec b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/pod_dependency/pod_dependency.podspec index e1f4ce85ff6..efdbea09fa1 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/pod_dependency/pod_dependency.podspec +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/pod_dependency/pod_dependency.podspec @@ -8,4 +8,6 @@ Pod::Spec.new do |spec| spec.summary = 'foo' spec.source_files = 'src/*' spec.public_header_files = 'src/*.h' + + spec.ios.deployment_target = '11.0' end \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/subspec_dependency/subspec_dependency.podspec b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/subspec_dependency/subspec_dependency.podspec index 343c53ccc09..db61a9b5e46 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/subspec_dependency/subspec_dependency.podspec +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/subspec_dependency/subspec_dependency.podspec @@ -12,4 +12,6 @@ Pod::Spec.new do |spec| core.source_files = "src/*" core.public_header_files = "src/*.h" end + + spec.ios.deployment_target = '11.0' end \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt index 3296a52a4e7..80f76d5d342 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt @@ -537,6 +537,7 @@ open class KotlinCocoapodsPlugin : Plugin { task.family.set(family) task.platformSettings.set(platformSettings) task.pods.set(cocoapodsExtension.pods) + task.xcodeVersion.set(Xcode?.currentVersion) } project.registerTask(family.toPodInstallSyntheticTaskName) { task -> diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt index ef530bd6584..ec22f705de4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt @@ -14,6 +14,7 @@ import org.gradle.api.tasks.* import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.* import org.jetbrains.kotlin.gradle.plugin.cocoapods.cocoapodsBuildDirs import org.jetbrains.kotlin.gradle.plugin.cocoapods.platformLiteral +import org.jetbrains.kotlin.gradle.utils.XcodeVersion import org.jetbrains.kotlin.konan.target.Family import java.io.File @@ -49,6 +50,9 @@ abstract class PodGenTask : CocoapodsTask() { @get:Nested internal abstract val pods: ListProperty + @get:Input + internal abstract val xcodeVersion: Property + @get:OutputFile val podfile: Provider = family.map { project.cocoapodsBuildDirs.synthetic(it).resolve("Podfile") } @@ -59,18 +63,18 @@ abstract class PodGenTask : CocoapodsTask() { val podfile = this.podfile.get() podfile.createNewFile() - val podfileContent = getPodfileContent(specRepos, family.get().platformLiteral) + val podfileContent = getPodfileContent(specRepos, family.get()) podfile.writeText(podfileContent) } - private fun getPodfileContent(specRepos: Collection, xcodeTarget: String) = + private fun getPodfileContent(specRepos: Collection, family: Family) = buildString { specRepos.forEach { appendLine("source '$it'") } - appendLine("target '$xcodeTarget' do") + appendLine("target '${family.platformLiteral}' do") if (useLibraries.get().not()) { appendLine("\tuse_frameworks!") } @@ -97,20 +101,55 @@ abstract class PodGenTask : CocoapodsTask() { } }.forEach { appendLine("\t$it") } appendLine("end\n") - //disable signing for all synthetic pods KT-54314 - append( + + appendLine( """ - post_install do |installer| - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['EXPANDED_CODE_SIGN_IDENTITY'] = "" - config.build_settings['CODE_SIGNING_REQUIRED'] = "NO" - config.build_settings['CODE_SIGNING_ALLOWED'] = "NO" - end - end - end - """.trimIndent() + |post_install do |installer| + | installer.pods_project.targets.each do |target| + | target.build_configurations.each do |config| + | + | # Disable signing for all synthetic pods KT-54314 + | config.build_settings['EXPANDED_CODE_SIGN_IDENTITY'] = "" + | config.build_settings['CODE_SIGNING_REQUIRED'] = "NO" + | config.build_settings['CODE_SIGNING_ALLOWED'] = "NO" + | ${insertXcode143DeploymentTargetWorkarounds(family)} + | end + | end + |end + """.trimMargin() ) - appendLine() } -} + + private fun insertXcode143DeploymentTargetWorkarounds(family: Family): String { + if (xcodeVersion.get() < XcodeVersion(14, 3)) { + return "" + } + + class Spec(val property: String, val major: Int, val minor: Int) + + val minDeploymentTargetSpec = when (family) { + Family.IOS -> Spec("IPHONEOS_DEPLOYMENT_TARGET", 11, 0) + Family.OSX -> Spec("MACOSX_DEPLOYMENT_TARGET", 10, 13) + Family.TVOS -> Spec("TVOS_DEPLOYMENT_TARGET", 11, 0) + Family.WATCHOS -> Spec("WATCHOS_DEPLOYMENT_TARGET", 4, 0) + else -> error("Family $family is not an Apple platform") + } + + return minDeploymentTargetSpec.run { + """ + | + | deployment_target_split = config.build_settings['$property']&.split('.') + | deployment_target_major = deployment_target_split&.first&.to_i + | deployment_target_minor = deployment_target_split&.second&.to_i + | + | if deployment_target_major && deployment_target_minor then + | if deployment_target_major < $major || (deployment_target_major == $major && deployment_target_minor < $minor) then + | version = "#{$major}.#{$minor}" + | puts "Deployment target for #{target} #{config} has been raised to #{version}. See KT-57741 for more details" + | config.build_settings['$property'] = version + | end + | end + """.trimMargin() + } + } +} \ No newline at end of file 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 index 28a33a5d546..467594f2dfa 100644 --- 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 @@ -15,21 +15,42 @@ import org.jetbrains.kotlin.konan.target.Family.* import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.util.capitalizeDecapitalize.toUpperCaseAsciiOnly +import java.io.Serializable @InternalKotlinGradlePluginApi val Xcode = XcodeUtils.INSTANCE +@InternalKotlinGradlePluginApi +data class XcodeVersion(val major: Int, val minor: Int) : Serializable, Comparable { + override fun compareTo(other: XcodeVersion): Int { + return when (val majorComparison = major.compareTo(other.major)) { + 0 -> minor.compareTo(other.minor) + else -> majorComparison + } + } +} + @InternalKotlinGradlePluginApi class XcodeUtils private constructor() { companion object { internal val INSTANCE: XcodeUtils? = if (HostManager.hostIsMac) XcodeUtils() else null + + internal fun parseFromXcodebuild(output: String): XcodeVersion? { + val version = output.lines()[0].removePrefix("Xcode ") + val split = version.split("(\\s+|\\.|-)".toRegex()) + return XcodeVersion( + major = split[0].toIntOrNull() ?: return null, + minor = split.getOrNull(1)?.toIntOrNull() ?: return null, + ) + + } } - val currentVersion: String by lazy { + val currentVersion: XcodeVersion by lazy { val out = runCommand(listOf("/usr/bin/xcrun", "xcodebuild", "-version")) - out.lines()[0].removePrefix("Xcode ") + parseFromXcodebuild(out) ?: XcodeVersion(1, 0) } private val defaultTestDevices: Map by lazy { @@ -66,7 +87,7 @@ class XcodeUtils private constructor() { fun getDefaultTestDeviceId(target: KonanTarget): String? = defaultTestDevices[target.family] fun defaultBitcodeEmbeddingMode(target: KonanTarget, buildType: NativeBuildType): BitcodeEmbeddingMode { - if (currentVersion.split(".")[0].toInt() < 14) { + if (currentVersion.major < 14) { if (target.family in listOf(IOS, WATCHOS, TVOS) && target.architecture in listOf(ARM32, ARM64)) { when (buildType) { NativeBuildType.RELEASE -> return BitcodeEmbeddingMode.BITCODE diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/utils/XcodeVersionTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/utils/XcodeVersionTest.kt new file mode 100644 index 00000000000..9e8d1e1ae4a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/utils/XcodeVersionTest.kt @@ -0,0 +1,31 @@ +/* + * 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.utils + +import org.junit.Test +import kotlin.test.assertEquals + +class XcodeVersionTest { + @Test + fun testParsing() { + assertEquals(XcodeVersion(14, 1), XcodeUtils.parseFromXcodebuild("Xcode 14.1\nBuild version 14B47b\n")) + assertEquals(XcodeVersion(14, 3), XcodeUtils.parseFromXcodebuild("Xcode 14.3\nBuild version 14E222b\n")) + assertEquals(XcodeVersion(1, 0), XcodeUtils.parseFromXcodebuild("Xcode 1.0 RC\nBuild version 7B85\n")) + assertEquals(XcodeVersion(1, 15), XcodeUtils.parseFromXcodebuild("Xcode 1.15-RC\nBuild version 7B85\n")) + assertEquals(XcodeVersion(14, 1), XcodeUtils.parseFromXcodebuild("Xcode 14.1.1.3.2.1.123\nBuild version 14B47b\n")) + assertEquals(null, XcodeUtils.parseFromXcodebuild("Xcode 14\nBuild version 14B47b\n")) + assertEquals(null, XcodeUtils.parseFromXcodebuild("XC 14.1\nBuild version 14B47b\n")) + } + + @Test + fun testComparison() { + assert(XcodeVersion(3, 2) == XcodeVersion(3, 2)) + assert(XcodeVersion(3, 2) > XcodeVersion(2, 14)) + assert(XcodeVersion(3, 2) > XcodeVersion(3, 1)) + assert(XcodeVersion(3, 2) < XcodeVersion(3, 3)) + assert(XcodeVersion(4, 0) > XcodeVersion(3, 3)) + } +} \ No newline at end of file