Skip to content

Commit c2e092f

Browse files
daugeldaugeSpace Team
authored and
Space Team
committed
[Gradle] Fix invalidating iOS framework after import
^KT-49430 Verification Pending
1 parent 8a34d1f commit c2e092f

File tree

5 files changed

+150
-35
lines changed

5 files changed

+150
-35
lines changed

libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,75 @@ class CocoaPodsIT : BaseGradleIT() {
397397
project.testWithWrapper(dummyTaskName)
398398
}
399399

400+
@Test
401+
fun testImportUTDAfterLinkingFramework() {
402+
val linkTaskName = ":linkPodDebugFrameworkIOS"
403+
project.gradleBuildScript().appendToCocoapodsBlock("""
404+
framework {
405+
baseName = "kotlin-library"
406+
}
407+
name = "kotlin-library"
408+
podfile = project.file("ios-app/Podfile")
409+
""".trimIndent())
410+
411+
412+
hooks.addHook {
413+
assertTasksExecuted(dummyTaskName)
414+
assertTasksExecuted(podInstallTaskName)
415+
}
416+
project.testImport()
417+
418+
hooks.rewriteHooks {
419+
assertTasksExecuted(linkTaskName)
420+
}
421+
project.testWithWrapper(linkTaskName)
422+
423+
hooks.rewriteHooks {
424+
assertTasksUpToDate(dummyTaskName)
425+
assertTasksUpToDate(podInstallTaskName)
426+
}
427+
project.testImport()
428+
}
429+
430+
@Test
431+
fun testChangeFrameworkTypeUTD() {
432+
project.gradleBuildScript().appendToCocoapodsBlock("""
433+
framework {
434+
baseName = "kotlin-library"
435+
}
436+
name = "kotlin-library"
437+
podfile = project.file("ios-app/Podfile")
438+
""".trimIndent())
439+
440+
hooks.addHook {
441+
assertTasksExecuted(dummyTaskName)
442+
assertTasksExecuted(podInstallTaskName)
443+
}
444+
project.testImport()
445+
446+
hooks.rewriteHooks {
447+
assertTasksUpToDate(dummyTaskName)
448+
assertTasksUpToDate(podInstallTaskName)
449+
}
450+
project.testImport()
451+
452+
project.gradleBuildScript().appendToFrameworkBlock("isStatic = true")
453+
454+
hooks.rewriteHooks {
455+
assertTasksExecuted(dummyTaskName)
456+
assertTasksExecuted(podInstallTaskName)
457+
}
458+
project.testImport()
459+
460+
hooks.rewriteHooks {
461+
assertTasksUpToDate(dummyTaskName)
462+
assertTasksUpToDate(podInstallTaskName)
463+
}
464+
project.testImport()
465+
}
466+
467+
468+
400469
@Test
401470
fun basicUTDTest() {
402471
val tasks = listOf(
@@ -771,10 +840,10 @@ class CocoaPodsIT : BaseGradleIT() {
771840
fun testUseDynamicFramework() {
772841
with(project) {
773842
gradleBuildScript().addPod(defaultPodName, produceGitBlock(defaultPodRepo))
774-
gradleBuildScript().appendToFrameworkBlock("isStatic=false")
843+
gradleBuildScript().appendToFrameworkBlock("isStatic = false")
775844
hooks.addHook {
776845
// Check that an output framework is a dynamic framework
777-
val framework = fileInWorkingDir("build/cocoapods/framework/cocoapods.framework/cocoapods")
846+
val framework = fileInWorkingDir("build/bin/iOS/podDebugFramework/cocoapods.framework/cocoapods")
778847
with(runProcess(listOf("file", framework.absolutePath), projectDir, environmentVariables = getEnvs())) {
779848
assertTrue(isSuccessful)
780849
assertTrue(output.contains("dynamically linked shared library"))
@@ -788,6 +857,27 @@ class CocoaPodsIT : BaseGradleIT() {
788857
}
789858
}
790859

860+
@Test
861+
fun testUseStaticFramework() {
862+
with(project) {
863+
gradleBuildScript().addPod(defaultPodName, produceGitBlock(defaultPodRepo))
864+
gradleBuildScript().appendToFrameworkBlock("isStatic = true")
865+
hooks.addHook {
866+
// Check that an output framework is a static framework
867+
val framework = fileInWorkingDir("build/bin/iOS/podDebugFramework/cocoapods.framework/cocoapods")
868+
with(runProcess(listOf("file", framework.absolutePath), projectDir, environmentVariables = getEnvs())) {
869+
assertTrue(isSuccessful)
870+
assertTrue(output.contains("current ar archive random library"))
871+
}
872+
}
873+
874+
test(
875+
"linkPodDebugFrameworkIOS",
876+
"-Pkotlin.native.cocoapods.generate.wrapper=true"
877+
)
878+
}
879+
}
880+
791881
@Test
792882
fun testCocoapodsWithRegularFrameworkDefinition() {
793883
with(project) {

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje
121121
anyFramework
122122
}
123123

124-
internal val podFrameworkName = anyPodFramework.map { it.baseName }
124+
internal val podFrameworkName = anyPodFramework.map { it.baseName.asValidFrameworkName() }
125125
internal val podFrameworkIsStatic = anyPodFramework.map { it.isStatic }
126126

127127
/**

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ internal class CocoapodsBuildDirs(val project: Project) {
5353
val framework: File
5454
get() = root.resolve("framework")
5555

56+
val dummyFramework: File
57+
get() = root.resolve("dummy.framework")
58+
5659
val defs: File
5760
get() = root.resolve("defs")
5861

@@ -365,18 +368,16 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
365368
project: Project,
366369
cocoapodsExtension: CocoapodsExtension
367370
) {
368-
project.tasks.register(DUMMY_FRAMEWORK_TASK_NAME, DummyFrameworkTask::class.java) {
369-
it.frameworkName = cocoapodsExtension.podFrameworkName
370-
it.useDynamicFramework = cocoapodsExtension.podFrameworkIsStatic.map { isStatic -> !isStatic }
371+
project.registerTask<DummyFrameworkTask>(DUMMY_FRAMEWORK_TASK_NAME) { task ->
372+
task.frameworkName.set(cocoapodsExtension.podFrameworkName)
373+
task.useStaticFramework.set(cocoapodsExtension.podFrameworkIsStatic)
371374
}
372375
}
373376

374377
private fun registerPodspecTask(
375378
project: Project,
376379
cocoapodsExtension: CocoapodsExtension
377380
) {
378-
val dummyFrameworkTaskProvider = project.tasks.named(DUMMY_FRAMEWORK_TASK_NAME)
379-
380381
project.tasks.register(POD_SPEC_TASK_NAME, PodspecTask::class.java) {
381382
it.group = TASK_GROUP
382383
it.description = "Generates a podspec file for CocoaPods import"
@@ -396,7 +397,6 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
396397
it.osx = project.provider { cocoapodsExtension.osx }
397398
it.tvos = project.provider { cocoapodsExtension.tvos }
398399
it.watchos = project.provider { cocoapodsExtension.watchos }
399-
it.dependsOn(dummyFrameworkTaskProvider)
400400
val generateWrapper = project.findProperty(GENERATE_WRAPPER_PROPERTY)?.toString()?.toBoolean() ?: false
401401
if (generateWrapper) {
402402
it.dependsOn(":wrapper")
@@ -461,6 +461,7 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
461461
cocoapodsExtension: CocoapodsExtension
462462
) {
463463
val podspecTaskProvider = project.tasks.named<PodspecTask>(POD_SPEC_TASK_NAME)
464+
val dummyFrameworkTaskProvider = project.tasks.named<DummyFrameworkTask>(DUMMY_FRAMEWORK_TASK_NAME)
464465
project.registerTask<PodInstallTask>(POD_INSTALL_TASK_NAME) { task ->
465466
task.group = TASK_GROUP
466467
task.description = "Invokes `pod install` call within Podfile location directory"
@@ -469,6 +470,7 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
469470
task.frameworkName.set(cocoapodsExtension.podFrameworkName)
470471
task.specRepos.set(project.provider { cocoapodsExtension.specRepos })
471472
task.pods.set(cocoapodsExtension.pods)
473+
task.dummyFramework.set(dummyFrameworkTaskProvider.map { it.outputFramework.get() })
472474
task.dependsOn(podspecTaskProvider)
473475
}
474476
}
@@ -496,12 +498,12 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
496498

497499
val podGenTask = project.registerTask<PodGenTask>(family.toPodGenTaskName) { task ->
498500
task.description = "Сreates a synthetic Xcode project to retrieve CocoaPods dependencies"
499-
task.podspec = podspecTaskProvider.map { it.outputFile }
500-
task.podName = project.provider { cocoapodsExtension.name }
501-
task.useLibraries = project.provider { cocoapodsExtension.useLibraries }
502-
task.specRepos = project.provider { cocoapodsExtension.specRepos }
503-
task.family = family
504-
task.platformSettings = platformSettings
501+
task.podspec.set(podspecTaskProvider.map { it.outputFile })
502+
task.podName.set(project.provider { cocoapodsExtension.name })
503+
task.useLibraries.set(project.provider { cocoapodsExtension.useLibraries })
504+
task.specRepos.set(project.provider { cocoapodsExtension.specRepos })
505+
task.family.set(family)
506+
task.platformSettings.set(platformSettings)
505507
task.pods.set(cocoapodsExtension.pods)
506508
}
507509

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/AdvancedCocoapodsTasks.kt

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ abstract class AbstractPodInstallTask : CocoapodsTask() {
5656
@get:InputFile
5757
abstract val podfile: Property<File?>
5858

59+
@get:Internal
5960
protected val workingDir: Provider<File> = podfile.map { file: File? ->
6061
requireNotNull(file) { "Task outputs shouldn't be queried if it's skipped" }.parentFile
6162
}
@@ -92,7 +93,6 @@ abstract class AbstractPodInstallTask : CocoapodsTask() {
9293

9394
abstract class PodInstallTask : AbstractPodInstallTask() {
9495

95-
9696
@get:Optional
9797
@get:InputFile
9898
abstract val podspec: Property<File?>
@@ -106,6 +106,32 @@ abstract class PodInstallTask : AbstractPodInstallTask() {
106106
@get:Nested
107107
abstract val pods: ListProperty<CocoapodsDependency>
108108

109+
@get:InputDirectory
110+
abstract val dummyFramework: Property<File>
111+
112+
private val framework = project.provider { project.cocoapodsBuildDirs.framework.resolve("${frameworkName.get()}.framework") }
113+
private val tmpFramework = dummyFramework.map { dummy -> dummy.parentFile.resolve("tmp.framework").also { it.deleteOnExit() } }
114+
115+
override fun doPodInstall() {
116+
// We always need to execute 'pod install' with the dummy framework because the one left from a previous build
117+
// may have a wrong linkage type. So we temporarily swap them, run 'pod install' and then swap them back
118+
framework.rename(tmpFramework)
119+
dummyFramework.rename(framework)
120+
super.doPodInstall()
121+
framework.rename(dummyFramework)
122+
tmpFramework.rename(framework)
123+
}
124+
125+
private fun Provider<File>.rename(dest: Provider<File>) = get().rename(dest.get())
126+
127+
private fun File.rename(dest: File) {
128+
if (!exists()) {
129+
mkdirs()
130+
}
131+
132+
check(renameTo(dest)) { "Can't rename '${this}' to '${dest}'" }
133+
}
134+
109135
override fun handleError(retCode: Int, error: String, process: Process): String? {
110136
val specReposMessages = MissingSpecReposMessage(specRepos.get()).missingMessage
111137
val cocoapodsMessages = pods.get().map { MissingCocoapodsMessage(it).missingMessage }

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/CocoapodsTasks.kt

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -236,30 +236,27 @@ open class PodspecTask : DefaultTask() {
236236
* So we create a dummy static framework to allow CocoaPods install our pod correctly
237237
* and then replace it with the real one during a real build process.
238238
*/
239-
open class DummyFrameworkTask : DefaultTask() {
239+
abstract class DummyFrameworkTask : DefaultTask() {
240240

241-
@OutputDirectory
242-
val destinationDir = project.cocoapodsBuildDirs.framework
243-
244-
@Input
245-
lateinit var frameworkName: Provider<String>
241+
@get:Input
242+
abstract val frameworkName: Property<String>
246243

247-
@Input
248-
lateinit var useDynamicFramework: Provider<Boolean>
244+
@get:Input
245+
abstract val useStaticFramework: Property<Boolean>
249246

250-
private val frameworkDir: File
251-
get() = destinationDir.resolve("${frameworkName.get()}.framework")
247+
@get:OutputDirectory
248+
val outputFramework: Provider<File> = project.provider { project.cocoapodsBuildDirs.dummyFramework }
252249

253-
private val dummyFrameworkPath: String
250+
private val dummyFrameworkResource: String
254251
get() {
255-
val staticOrDynamic = if (useDynamicFramework.get()) "dynamic" else "static"
252+
val staticOrDynamic = if (!useStaticFramework.get()) "dynamic" else "static"
256253
return "/cocoapods/$staticOrDynamic/dummy.framework/"
257254
}
258255

259256
private fun copyResource(from: String, to: File) {
260257
to.parentFile.mkdirs()
261258
to.outputStream().use { file ->
262-
javaClass.getResourceAsStream(from).use { resource ->
259+
javaClass.getResourceAsStream(from)!!.use { resource ->
263260
resource.copyTo(file)
264261
}
265262
}
@@ -268,7 +265,7 @@ open class DummyFrameworkTask : DefaultTask() {
268265
private fun copyTextResource(from: String, to: File, transform: (String) -> String = { it }) {
269266
to.parentFile.mkdirs()
270267
to.printWriter().use { file ->
271-
javaClass.getResourceAsStream(from).use {
268+
javaClass.getResourceAsStream(from)!!.use {
272269
it.reader().forEachLine { str ->
273270
file.println(transform(str))
274271
}
@@ -278,24 +275,24 @@ open class DummyFrameworkTask : DefaultTask() {
278275

279276
private fun copyFrameworkFile(relativeFrom: String, relativeTo: String = relativeFrom) =
280277
copyResource(
281-
"$dummyFrameworkPath$relativeFrom",
282-
frameworkDir.resolve(relativeTo)
278+
"$dummyFrameworkResource$relativeFrom",
279+
outputFramework.get().resolve(relativeTo)
283280
)
284281

285282
private fun copyFrameworkTextFile(
286283
relativeFrom: String,
287284
relativeTo: String = relativeFrom,
288285
transform: (String) -> String = { it }
289286
) = copyTextResource(
290-
"$dummyFrameworkPath$relativeFrom",
291-
frameworkDir.resolve(relativeTo),
287+
"$dummyFrameworkResource$relativeFrom",
288+
outputFramework.get().resolve(relativeTo),
292289
transform
293290
)
294291

295292
@TaskAction
296293
fun create() {
297294
// Reset the destination directory
298-
with(destinationDir) {
295+
with(outputFramework.get()) {
299296
deleteRecursively()
300297
mkdirs()
301298
}

0 commit comments

Comments
 (0)