[Gradle] Fix invalidating iOS framework after import

^KT-49430 Verification Pending
This commit is contained in:
Artem Daugel-Dauge
2023-01-18 21:59:38 +01:00
committed by Space Team
parent 8a34d1f430
commit c2e092fe4a
5 changed files with 150 additions and 35 deletions
@@ -397,6 +397,75 @@ class CocoaPodsIT : BaseGradleIT() {
project.testWithWrapper(dummyTaskName)
}
@Test
fun testImportUTDAfterLinkingFramework() {
val linkTaskName = ":linkPodDebugFrameworkIOS"
project.gradleBuildScript().appendToCocoapodsBlock("""
framework {
baseName = "kotlin-library"
}
name = "kotlin-library"
podfile = project.file("ios-app/Podfile")
""".trimIndent())
hooks.addHook {
assertTasksExecuted(dummyTaskName)
assertTasksExecuted(podInstallTaskName)
}
project.testImport()
hooks.rewriteHooks {
assertTasksExecuted(linkTaskName)
}
project.testWithWrapper(linkTaskName)
hooks.rewriteHooks {
assertTasksUpToDate(dummyTaskName)
assertTasksUpToDate(podInstallTaskName)
}
project.testImport()
}
@Test
fun testChangeFrameworkTypeUTD() {
project.gradleBuildScript().appendToCocoapodsBlock("""
framework {
baseName = "kotlin-library"
}
name = "kotlin-library"
podfile = project.file("ios-app/Podfile")
""".trimIndent())
hooks.addHook {
assertTasksExecuted(dummyTaskName)
assertTasksExecuted(podInstallTaskName)
}
project.testImport()
hooks.rewriteHooks {
assertTasksUpToDate(dummyTaskName)
assertTasksUpToDate(podInstallTaskName)
}
project.testImport()
project.gradleBuildScript().appendToFrameworkBlock("isStatic = true")
hooks.rewriteHooks {
assertTasksExecuted(dummyTaskName)
assertTasksExecuted(podInstallTaskName)
}
project.testImport()
hooks.rewriteHooks {
assertTasksUpToDate(dummyTaskName)
assertTasksUpToDate(podInstallTaskName)
}
project.testImport()
}
@Test
fun basicUTDTest() {
val tasks = listOf(
@@ -771,10 +840,10 @@ class CocoaPodsIT : BaseGradleIT() {
fun testUseDynamicFramework() {
with(project) {
gradleBuildScript().addPod(defaultPodName, produceGitBlock(defaultPodRepo))
gradleBuildScript().appendToFrameworkBlock("isStatic=false")
gradleBuildScript().appendToFrameworkBlock("isStatic = false")
hooks.addHook {
// Check that an output framework is a dynamic framework
val framework = fileInWorkingDir("build/cocoapods/framework/cocoapods.framework/cocoapods")
val framework = fileInWorkingDir("build/bin/iOS/podDebugFramework/cocoapods.framework/cocoapods")
with(runProcess(listOf("file", framework.absolutePath), projectDir, environmentVariables = getEnvs())) {
assertTrue(isSuccessful)
assertTrue(output.contains("dynamically linked shared library"))
@@ -788,6 +857,27 @@ class CocoaPodsIT : BaseGradleIT() {
}
}
@Test
fun testUseStaticFramework() {
with(project) {
gradleBuildScript().addPod(defaultPodName, produceGitBlock(defaultPodRepo))
gradleBuildScript().appendToFrameworkBlock("isStatic = true")
hooks.addHook {
// Check that an output framework is a static framework
val framework = fileInWorkingDir("build/bin/iOS/podDebugFramework/cocoapods.framework/cocoapods")
with(runProcess(listOf("file", framework.absolutePath), projectDir, environmentVariables = getEnvs())) {
assertTrue(isSuccessful)
assertTrue(output.contains("current ar archive random library"))
}
}
test(
"linkPodDebugFrameworkIOS",
"-Pkotlin.native.cocoapods.generate.wrapper=true"
)
}
}
@Test
fun testCocoapodsWithRegularFrameworkDefinition() {
with(project) {
@@ -121,7 +121,7 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje
anyFramework
}
internal val podFrameworkName = anyPodFramework.map { it.baseName }
internal val podFrameworkName = anyPodFramework.map { it.baseName.asValidFrameworkName() }
internal val podFrameworkIsStatic = anyPodFramework.map { it.isStatic }
/**
@@ -53,6 +53,9 @@ internal class CocoapodsBuildDirs(val project: Project) {
val framework: File
get() = root.resolve("framework")
val dummyFramework: File
get() = root.resolve("dummy.framework")
val defs: File
get() = root.resolve("defs")
@@ -365,9 +368,9 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
project: Project,
cocoapodsExtension: CocoapodsExtension
) {
project.tasks.register(DUMMY_FRAMEWORK_TASK_NAME, DummyFrameworkTask::class.java) {
it.frameworkName = cocoapodsExtension.podFrameworkName
it.useDynamicFramework = cocoapodsExtension.podFrameworkIsStatic.map { isStatic -> !isStatic }
project.registerTask<DummyFrameworkTask>(DUMMY_FRAMEWORK_TASK_NAME) { task ->
task.frameworkName.set(cocoapodsExtension.podFrameworkName)
task.useStaticFramework.set(cocoapodsExtension.podFrameworkIsStatic)
}
}
@@ -375,8 +378,6 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
project: Project,
cocoapodsExtension: CocoapodsExtension
) {
val dummyFrameworkTaskProvider = project.tasks.named(DUMMY_FRAMEWORK_TASK_NAME)
project.tasks.register(POD_SPEC_TASK_NAME, PodspecTask::class.java) {
it.group = TASK_GROUP
it.description = "Generates a podspec file for CocoaPods import"
@@ -396,7 +397,6 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
it.osx = project.provider { cocoapodsExtension.osx }
it.tvos = project.provider { cocoapodsExtension.tvos }
it.watchos = project.provider { cocoapodsExtension.watchos }
it.dependsOn(dummyFrameworkTaskProvider)
val generateWrapper = project.findProperty(GENERATE_WRAPPER_PROPERTY)?.toString()?.toBoolean() ?: false
if (generateWrapper) {
it.dependsOn(":wrapper")
@@ -461,6 +461,7 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
cocoapodsExtension: CocoapodsExtension
) {
val podspecTaskProvider = project.tasks.named<PodspecTask>(POD_SPEC_TASK_NAME)
val dummyFrameworkTaskProvider = project.tasks.named<DummyFrameworkTask>(DUMMY_FRAMEWORK_TASK_NAME)
project.registerTask<PodInstallTask>(POD_INSTALL_TASK_NAME) { task ->
task.group = TASK_GROUP
task.description = "Invokes `pod install` call within Podfile location directory"
@@ -469,6 +470,7 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
task.frameworkName.set(cocoapodsExtension.podFrameworkName)
task.specRepos.set(project.provider { cocoapodsExtension.specRepos })
task.pods.set(cocoapodsExtension.pods)
task.dummyFramework.set(dummyFrameworkTaskProvider.map { it.outputFramework.get() })
task.dependsOn(podspecTaskProvider)
}
}
@@ -496,12 +498,12 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
val podGenTask = project.registerTask<PodGenTask>(family.toPodGenTaskName) { task ->
task.description = "Сreates a synthetic Xcode project to retrieve CocoaPods dependencies"
task.podspec = podspecTaskProvider.map { it.outputFile }
task.podName = project.provider { cocoapodsExtension.name }
task.useLibraries = project.provider { cocoapodsExtension.useLibraries }
task.specRepos = project.provider { cocoapodsExtension.specRepos }
task.family = family
task.platformSettings = platformSettings
task.podspec.set(podspecTaskProvider.map { it.outputFile })
task.podName.set(project.provider { cocoapodsExtension.name })
task.useLibraries.set(project.provider { cocoapodsExtension.useLibraries })
task.specRepos.set(project.provider { cocoapodsExtension.specRepos })
task.family.set(family)
task.platformSettings.set(platformSettings)
task.pods.set(cocoapodsExtension.pods)
}
@@ -56,6 +56,7 @@ abstract class AbstractPodInstallTask : CocoapodsTask() {
@get:InputFile
abstract val podfile: Property<File?>
@get:Internal
protected val workingDir: Provider<File> = podfile.map { file: File? ->
requireNotNull(file) { "Task outputs shouldn't be queried if it's skipped" }.parentFile
}
@@ -92,7 +93,6 @@ abstract class AbstractPodInstallTask : CocoapodsTask() {
abstract class PodInstallTask : AbstractPodInstallTask() {
@get:Optional
@get:InputFile
abstract val podspec: Property<File?>
@@ -106,6 +106,32 @@ abstract class PodInstallTask : AbstractPodInstallTask() {
@get:Nested
abstract val pods: ListProperty<CocoapodsDependency>
@get:InputDirectory
abstract val dummyFramework: Property<File>
private val framework = project.provider { project.cocoapodsBuildDirs.framework.resolve("${frameworkName.get()}.framework") }
private val tmpFramework = dummyFramework.map { dummy -> dummy.parentFile.resolve("tmp.framework").also { it.deleteOnExit() } }
override fun doPodInstall() {
// We always need to execute 'pod install' with the dummy framework because the one left from a previous build
// may have a wrong linkage type. So we temporarily swap them, run 'pod install' and then swap them back
framework.rename(tmpFramework)
dummyFramework.rename(framework)
super.doPodInstall()
framework.rename(dummyFramework)
tmpFramework.rename(framework)
}
private fun Provider<File>.rename(dest: Provider<File>) = get().rename(dest.get())
private fun File.rename(dest: File) {
if (!exists()) {
mkdirs()
}
check(renameTo(dest)) { "Can't rename '${this}' to '${dest}'" }
}
override fun handleError(retCode: Int, error: String, process: Process): String? {
val specReposMessages = MissingSpecReposMessage(specRepos.get()).missingMessage
val cocoapodsMessages = pods.get().map { MissingCocoapodsMessage(it).missingMessage }
@@ -236,30 +236,27 @@ open class PodspecTask : DefaultTask() {
* So we create a dummy static framework to allow CocoaPods install our pod correctly
* and then replace it with the real one during a real build process.
*/
open class DummyFrameworkTask : DefaultTask() {
abstract class DummyFrameworkTask : DefaultTask() {
@OutputDirectory
val destinationDir = project.cocoapodsBuildDirs.framework
@get:Input
abstract val frameworkName: Property<String>
@Input
lateinit var frameworkName: Provider<String>
@get:Input
abstract val useStaticFramework: Property<Boolean>
@Input
lateinit var useDynamicFramework: Provider<Boolean>
@get:OutputDirectory
val outputFramework: Provider<File> = project.provider { project.cocoapodsBuildDirs.dummyFramework }
private val frameworkDir: File
get() = destinationDir.resolve("${frameworkName.get()}.framework")
private val dummyFrameworkPath: String
private val dummyFrameworkResource: String
get() {
val staticOrDynamic = if (useDynamicFramework.get()) "dynamic" else "static"
val staticOrDynamic = if (!useStaticFramework.get()) "dynamic" else "static"
return "/cocoapods/$staticOrDynamic/dummy.framework/"
}
private fun copyResource(from: String, to: File) {
to.parentFile.mkdirs()
to.outputStream().use { file ->
javaClass.getResourceAsStream(from).use { resource ->
javaClass.getResourceAsStream(from)!!.use { resource ->
resource.copyTo(file)
}
}
@@ -268,7 +265,7 @@ open class DummyFrameworkTask : DefaultTask() {
private fun copyTextResource(from: String, to: File, transform: (String) -> String = { it }) {
to.parentFile.mkdirs()
to.printWriter().use { file ->
javaClass.getResourceAsStream(from).use {
javaClass.getResourceAsStream(from)!!.use {
it.reader().forEachLine { str ->
file.println(transform(str))
}
@@ -278,8 +275,8 @@ open class DummyFrameworkTask : DefaultTask() {
private fun copyFrameworkFile(relativeFrom: String, relativeTo: String = relativeFrom) =
copyResource(
"$dummyFrameworkPath$relativeFrom",
frameworkDir.resolve(relativeTo)
"$dummyFrameworkResource$relativeFrom",
outputFramework.get().resolve(relativeTo)
)
private fun copyFrameworkTextFile(
@@ -287,15 +284,15 @@ open class DummyFrameworkTask : DefaultTask() {
relativeTo: String = relativeFrom,
transform: (String) -> String = { it }
) = copyTextResource(
"$dummyFrameworkPath$relativeFrom",
frameworkDir.resolve(relativeTo),
"$dummyFrameworkResource$relativeFrom",
outputFramework.get().resolve(relativeTo),
transform
)
@TaskAction
fun create() {
// Reset the destination directory
with(destinationDir) {
with(outputFramework.get()) {
deleteRecursively()
mkdirs()
}