From a24119858f156e18bbf40714e645137ce016371b Mon Sep 17 00:00:00 2001 From: Artem Daugel-Dauge Date: Tue, 17 Jan 2023 18:32:54 +0000 Subject: [PATCH] Add linkOnly mode for pods ^KT-41830 Verification Pending Merge-request: KT-MR-8302 Merged-by: Artem Daugel-Dauge --- .../kotlin/gradle/native/CocoaPodsIT.kt | 52 +++++++++++++++++++ .../native/cocoapods/CocoapodsExtension.kt | 23 +++++++- .../native/cocoapods/KotlinCocoapodsPlugin.kt | 17 +++++- 3 files changed, 90 insertions(+), 2 deletions(-) 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 66c0f6d3a9c..6145bdb3739 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 @@ -1088,6 +1088,58 @@ class CocoaPodsIT : BaseGradleIT() { assertContains(manifestLines, "linkerOpts=-framework AFNetworking") } + @Test + fun testLinkOnlyPods() = with(project) { + gradleBuildScript().appendToCocoapodsBlock(""" + pod("AFNetworking") { linkOnly = true } + pod("SSZipArchive", linkOnly = true) + pod("SDWebImage/Core") + """.trimIndent()) + + build(":linkPodDebugFrameworkIOS", "-Pkotlin.native.cocoapods.generate.wrapper=true") { + assertSuccessful() + + assertTasksExecuted(":podBuildAFNetworkingIphonesimulator") + assertTasksExecuted(":podBuildSDWebImageIphonesimulator") + assertTasksExecuted(":podBuildSSZipArchiveIphonesimulator") + + assertTasksExecuted(":cinteropSDWebImageIOS") + assertTasksNotRegistered(":cinteropAFNetworkingIOS") + assertTasksNotRegistered(":cinteropSSZipArchiveIOS") + + assertContains(""" + | -linker-option + | -framework + | -linker-option + | AFNetworking + """.trimMargin()) + + assertContains(""" + | -linker-option + | -framework + | -linker-option + | SSZipArchive + """.trimMargin()) + } + } + + @Test + fun testUsageLinkOnlyWithStaticFrameworkProducesMessage() = with(project) { + gradleBuildScript().appendToCocoapodsBlock(""" + framework { + isStatic = true + } + + pod("AFNetworking") { linkOnly = true } + """.trimIndent()) + + build(":linkPodDebugFrameworkIOS", "-Pkotlin.native.cocoapods.generate.wrapper=true") { + assertSuccessful() + + assertContains("Dependency on 'AFNetworking' with option 'linkOnly=true' is unused for building static frameworks") + } + } + // test configuration phase private class CustomHooks { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt index 1ca40309180..652d728390e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt @@ -157,9 +157,19 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje /** * Add a CocoaPods dependency to the pod built from this project. + * + * @param linkOnly designates that the pod will be used only for dynamic framework linking and not for the cinterops. Code from it won't + * be accessible for referencing from Kotlin but its native symbols will be visible while linking the framework. */ @JvmOverloads - fun pod(name: String, version: String? = null, path: File? = null, moduleName: String = name.asModuleName(), headers: String? = null) { + fun pod( + name: String, + version: String? = null, + path: File? = null, + moduleName: String = name.asModuleName(), + headers: String? = null, + linkOnly: Boolean = false, + ) { // Empty string will lead to an attempt to create two podDownload tasks. // One is original podDownload and second is podDownload + pod.name require(name.isNotEmpty()) { "Please provide not empty pod name to avoid ambiguity" } @@ -197,6 +207,7 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje this.headers = headers this.version = version source = podSource?.let { Path(it) } + this.linkOnly = linkOnly } ) } @@ -276,6 +287,16 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje @get:Internal var packageName: String = "cocoapods.$moduleName" + /** + * Designates that the pod will be used only for dynamic framework linking and not for the cinterops. Code from it won't be + * accessible for referencing from Kotlin but its native symbols will be visible while linking the framework. + * + * For static frameworks adding this flag is equivalent to removing the pod dependency entirely (because pods are not used for + * static framework linking). + */ + @get:Input + var linkOnly: Boolean = false + @Input override fun getName(): String = name 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 29e17e2f2e2..f6b0e672116 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 @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.gradle.targets.native.cocoapods.kotlinArtifactsPodsp import org.jetbrains.kotlin.gradle.targets.native.tasks.* import org.jetbrains.kotlin.gradle.targets.native.tasks.artifact.kotlinArtifactsExtension import org.jetbrains.kotlin.gradle.tasks.* +import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.gradle.utils.asValidTaskName import org.jetbrains.kotlin.gradle.utils.filesProvider import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName @@ -297,7 +298,7 @@ open class KotlinCocoapodsPlugin : Plugin { val moduleNames = mutableSetOf() cocoapodsExtension.pods.all { pod -> - if (moduleNames.contains(pod.moduleName)) { + if (pod.linkOnly || moduleNames.contains(pod.moduleName)) { return@all } moduleNames.add(pod.moduleName) @@ -743,6 +744,19 @@ open class KotlinCocoapodsPlugin : Plugin { } } + private fun checkLinkOnlyNotUsedWithStaticFramework(project: Project, cocoapodsExtension: CocoapodsExtension) { + project.runProjectConfigurationHealthCheckWhenEvaluated { + cocoapodsExtension.pods.all { pod -> + if (pod.linkOnly && cocoapodsExtension.podFrameworkIsStatic.get()) { + logger.warn(""" + Dependency on '${pod.name}' with option 'linkOnly=true' is unused for building static frameworks. + When using static linkage you will need to provide all dependencies for linking the framework into a final application. + """.trimIndent()) + } + } + } + } + override fun apply(project: Project): Unit = with(project) { pluginManager.withPlugin("kotlin-multiplatform") { @@ -774,6 +788,7 @@ open class KotlinCocoapodsPlugin : Plugin { } createInterops(project, kotlinExtension, cocoapodsExtension) configureLinkingOptions(project, cocoapodsExtension) + checkLinkOnlyNotUsedWithStaticFramework(project, cocoapodsExtension) } }