[Gradle] Support dependencies between pods in CocoaPods plugin

^KT-38749 Verification Pending
This commit is contained in:
Artem Daugel-Dauge
2023-01-26 23:46:54 +01:00
committed by Space Team
parent b36e1f1a5b
commit 4b66160afb
16 changed files with 287 additions and 3 deletions
@@ -1299,6 +1299,47 @@ class CocoaPodsIT : BaseGradleIT() {
}
}
@Test
fun `hierarchy of dependant pods compiles successfully`() = with(getProjectByName("native-cocoapods-dependant-pods")) {
build(
":compileKotlinIosX64",
"-Pkotlin.native.cocoapods.generate.wrapper=true",
) {
assertSuccessful()
}
}
@Test
fun `configuration fails when trying to depend on non-declared pod`() = with(getProjectByName("native-cocoapods-dependant-pods")) {
gradleBuildScript().appendToCocoapodsBlock("""
pod("Foo") { useInteropBindingFrom("JBNonExistent") }
""".trimIndent())
build(
":help",
"-Pkotlin.native.cocoapods.generate.wrapper=true",
) {
assertFailed()
assertContains("Couldn't find declaration of pod 'JBNonExistent' (interop-binding dependency of pod 'Foo')")
}
}
@Test
fun `configuration fails when dependant pods are in the wrong order`() = with(getProjectByName("native-cocoapods-dependant-pods")) {
gradleBuildScript().appendToCocoapodsBlock("""
pod("Foo") { useInteropBindingFrom("Bar") }
pod("Bar")
""".trimIndent())
build(
":help",
"-Pkotlin.native.cocoapods.generate.wrapper=true",
) {
assertFailed()
assertContains("Couldn't find declaration of pod 'Bar' (interop-binding dependency of pod 'Foo')")
}
}
// test configuration phase
private class CustomHooks {
@@ -0,0 +1,51 @@
plugins {
kotlin("multiplatform") version "<pluginMarkerVersion>"
kotlin("native.cocoapods") version "<pluginMarkerVersion>"
}
group = "org.jetbrains.kotlin.sample.native"
version = "1.0"
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
iosX64()
cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
version = "1.0"
ios.deploymentTarget = "14.1"
framework {
baseName = "shared"
}
pod("pod1", path = project.file("pod1"))
pod("pod2") {
source = path(project.file("pod2"))
useInteropBindingFrom("pod1")
extraOpts = listOf("-compiler-option", "-fmodules")
}
pod("pod3") {
source = path(project.file("pod3"))
interopBindingDependencies.add("pod1")
}
pod("pod4") {
source = path(project.file("pod4"))
useInteropBindingFrom("pod2")
useInteropBindingFrom("pod3")
extraOpts = listOf("-compiler-option", "-fmodules")
}
}
}
@@ -0,0 +1,4 @@
@interface Pod1 : NSObject
-(int)pod1;
-(void)name: (NSString *) firstName secondName: (NSString *) secondName;
@end
@@ -0,0 +1,13 @@
#include <pod1.h>
@implementation Pod1
-(int)pod1 {
return 42;
}
-(void)name: (NSString *) firstName secondName: (NSString *) secondName {
}
@end
@@ -0,0 +1,12 @@
Pod::Spec.new do |spec|
spec.name = 'pod1'
spec.version = '1.0'
spec.homepage = 'repro'
spec.source = { :git => "Not Published", :tag => "Cocoapods/#{spec.name}/#{spec.version}" }
spec.authors = ''
spec.license = ''
spec.summary = 'repro'
spec.source_files = '*.{h,m}'
spec.public_header_files = '*.h'
end
@@ -0,0 +1,14 @@
Pod::Spec.new do |spec|
spec.name = 'pod2'
spec.version = '1.0'
spec.homepage = 'repro'
spec.source = { :git => "Not Published", :tag => "Cocoapods/#{spec.name}/#{spec.version}" }
spec.authors = ''
spec.license = ''
spec.summary = 'repro'
spec.dependency 'pod1'
spec.swift_version = '5.0'
spec.source_files = '*.swift'
end
@@ -0,0 +1,9 @@
import pod1
@objc
open class Pod2: Pod1 {
@objc
public func pod2() -> Int32 {
return 33
}
}
@@ -0,0 +1,5 @@
#import "pod1/pod1.h"
@interface Pod3 : Pod1
-(int)pod3;
@end
@@ -0,0 +1,9 @@
#include <pod3.h>
@implementation Pod3
-(int)pod3 {
return 42;
}
@end
@@ -0,0 +1,14 @@
Pod::Spec.new do |spec|
spec.name = 'pod3'
spec.version = '1.0'
spec.homepage = 'repro'
spec.source = { :git => "Not Published", :tag => "Cocoapods/#{spec.name}/#{spec.version}" }
spec.authors = ''
spec.license = ''
spec.summary = 'repro'
spec.source_files = '*.{h,m}'
spec.public_header_files = '*.h'
spec.dependency 'pod1'
end
@@ -0,0 +1,15 @@
Pod::Spec.new do |spec|
spec.name = 'pod4'
spec.version = '1.0'
spec.homepage = 'repro'
spec.source = { :git => "Not Published", :tag => "Cocoapods/#{spec.name}/#{spec.version}" }
spec.authors = ''
spec.license = ''
spec.summary = 'repro'
spec.dependency 'pod2'
spec.dependency 'pod3'
spec.swift_version = '5.0'
spec.source_files = '*.swift'
end
@@ -0,0 +1,18 @@
import pod2
import pod3
@objc
public class Pod42: Pod2 {
@objc
public func pod4() -> String {
return "Hello"
}
}
@objc
public class Pod43: Pod3 {
@objc
public func pod4() -> String {
return "Hello"
}
}
@@ -0,0 +1,30 @@
import cocoapods.pod1.Pod1
import cocoapods.pod2.Pod2
import cocoapods.pod3.Pod3
import cocoapods.pod4.Pod42
import cocoapods.pod4.Pod43
fun printPod1(p: Pod1) {
println(p.pod1())
}
fun printPod2(p: Pod2) {
println("Hi from Kt!")
println(p.pod2())
printPod1(p)
}
fun printPod3(p: Pod3) {
printPod1(p)
}
fun printPods4(p42: Pod42, p43: Pod43) {
printPod2(p42)
printPod3(p43)
printPod1(p42)
printPod1(p43)
}
fun main() {
printPods4(Pod42(), Pod43())
}
@@ -20,7 +20,7 @@ import java.io.File
import java.net.URI
import javax.inject.Inject
@Suppress("unused") // Public API
@Suppress("unused", "MemberVisibilityCanBePrivate") // Public API
abstract class CocoapodsExtension @Inject constructor(private val project: Project) {
/**
* Configure version of the pod
@@ -292,6 +292,27 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje
@get:Input
var linkOnly: Boolean = false
/**
* Contains a list of dependencies to other pods. This list will be used while building an interop Kotlin-binding for the pod.
*
* @see useInteropBindingFrom
*/
@get:Input
val interopBindingDependencies: MutableList<String> = mutableListOf()
/**
* Specify that the pod depends on another pod **podName** and a Kotlin-binding for **podName** should be used while building
* a binding for the pod. This is necessary if you need to operate entities from **podName** and from the pod together, for
* instance pass an object from **podName** to the pod in Kotlin.
*
* A pod with the exact name must be declared before calling this function.
*
* @see interopBindingDependencies
*/
fun useInteropBindingFrom(podName: String) {
interopBindingDependencies.add(podName)
}
@Input
override fun getName(): String = name
@@ -7,6 +7,7 @@
package org.jetbrains.kotlin.gradle.plugin.cocoapods
import org.gradle.api.DefaultTask
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.ExtensionAware
@@ -326,12 +327,17 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
}
kotlinExtension.supportedTargets().all { target ->
target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).cinterops.create(pod.moduleName) { interop ->
val cinterops = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).cinterops
cinterops.create(pod.moduleName) { interop ->
val interopTask = project.tasks.getByPath(interop.interopProcessingTaskName)
val interopTask = project.tasks.named<CInteropProcess>(interop.interopProcessingTaskName).get()
interopTask.dependsOn(defTask)
pod.interopBindingDependencies.forEach { dependencyName ->
addPodDependencyToInterop(project, cocoapodsExtension, pod, cinterops, interop, dependencyName)
}
with(interop) {
defFileProperty.set(defTask.map { it.outputFile })
_packageNameProp.set(project.provider { pod.packageName })
@@ -364,6 +370,27 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
}
}
private fun addPodDependencyToInterop(
project: Project,
cocoapodsExtension: CocoapodsExtension,
pod: CocoapodsDependency,
cinterops: NamedDomainObjectContainer<DefaultCInteropSettings>,
interop: DefaultCInteropSettings,
dependencyName: String,
) {
val dependencyPod = cocoapodsExtension.pods.findByName(dependencyName)
?: error("Couldn't find declaration of pod '$dependencyName' (interop-binding dependency of pod '${pod.name}')")
val dependencyTaskName = cinterops.getByName(dependencyPod.moduleName).interopProcessingTaskName
val dependencyTask = project.tasks.named<CInteropProcess>(dependencyTaskName)
interop.dependencyFiles += project.files(dependencyTask.map { it.outputFile }).builtBy(dependencyTask)
dependencyPod.interopBindingDependencies.forEach { transitiveDependency ->
addPodDependencyToInterop(project, cocoapodsExtension, pod, cinterops, interop, transitiveDependency)
}
}
private fun registerDummyFrameworkTask(
project: Project,
cocoapodsExtension: CocoapodsExtension