From caaade1f4f91c657527d103e6b7a48fbb5cb267c Mon Sep 17 00:00:00 2001 From: Artem Daugel-Dauge Date: Fri, 23 Jun 2023 22:05:25 +0200 Subject: [PATCH] [Gradle] Fix invalidation framework after sync while preserving 'generateDummyFramework' task semantic ^KT-49430 Verification Pending --- .../kotlin/gradle/native/CocoaPodsIT.kt | 42 +++++++-- .../kotlin/gradle/native/CocoaPodsXcodeIT.kt | 89 +++++++++++++----- .../native/cocoapods/CocoapodsExtension.kt | 5 +- .../native/cocoapods/KotlinCocoapodsPlugin.kt | 12 +-- .../cocoapods/tasks/DummyFrameworkTask.kt | 58 ++++++++---- .../native/cocoapods/tasks/PodInstallTask.kt | 35 +------ .../jetbrains/kotlin/gradle/utils/MachO.kt | 59 ++++++++++++ .../kotlin/gradle/unitTests/MachOTest.kt | 67 +++++++++++++ .../resources/machoBinaries/dynamicFat | Bin 0 -> 24752 bytes .../resources/machoBinaries/dynamicLib | Bin 0 -> 66696 bytes .../resources/machoBinaries/staticFat | Bin 0 -> 8968 bytes .../resources/machoBinaries/staticLib | Bin 0 -> 11480 bytes 12 files changed, 278 insertions(+), 89 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/MachO.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/MachOTest.kt create mode 100755 libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/dynamicFat create mode 100755 libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/dynamicLib create mode 100755 libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/staticFat create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/staticLib 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 4233f96f739..859694c029f 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 @@ -17,10 +17,8 @@ import org.jetbrains.kotlin.gradle.util.assertProcessRunResult import org.jetbrains.kotlin.gradle.util.replaceText import org.jetbrains.kotlin.gradle.util.runProcess import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.condition.OS -import java.io.File import java.util.zip.ZipFile import kotlin.io.path.* import kotlin.test.* @@ -121,11 +119,19 @@ class CocoaPodsIT : KGPBaseTest() { } } - @DisplayName("UTD after linking framework") + @DisplayName("UTD after syncing framework") @GradleTest - fun testImportUTDAfterLinkingFramework(gradleVersion: GradleVersion) { - nativeProjectWithCocoapodsAndIosAppPodFile(gradleVersion = gradleVersion) { - val linkTaskName = ":linkPodDebugFrameworkIOS" + fun testImportUTDAfterSyncingFramework(gradleVersion: GradleVersion) { + val buildOptions = defaultBuildOptions.copy( + nativeOptions = defaultBuildOptions.nativeOptions.copy( + cocoapodsPlatform = "iphonesimulator", + cocoapodsArchs = "x86_64", + cocoapodsConfiguration = "Debug" + ) + ) + + nativeProjectWithCocoapodsAndIosAppPodFile(gradleVersion = gradleVersion, buildOptions = buildOptions) { + val syncTaskName = ":syncFramework" buildGradleKts.addCocoapodsBlock( """ @@ -142,12 +148,13 @@ class CocoaPodsIT : KGPBaseTest() { assertTasksExecuted(podInstallTaskName) } - buildWithCocoapodsWrapper(linkTaskName) { - assertTasksExecuted(linkTaskName) + buildWithCocoapodsWrapper(syncTaskName) { + assertTasksExecuted(syncTaskName) } buildWithCocoapodsWrapper(podImportTaskName) { - assertTasksUpToDate(dummyTaskName) + assertTasksExecuted(dummyTaskName) + assertOutputContains("Skipping dummy-framework generation because a dynamic framework is already present") assertTasksUpToDate(podInstallTaskName) } } @@ -156,7 +163,17 @@ class CocoaPodsIT : KGPBaseTest() { @DisplayName("Changing framework type and checks UTD") @GradleTest fun testChangeFrameworkTypeUTD(gradleVersion: GradleVersion) { - nativeProjectWithCocoapodsAndIosAppPodFile(gradleVersion = gradleVersion) { + val buildOptions = defaultBuildOptions.copy( + nativeOptions = defaultBuildOptions.nativeOptions.copy( + cocoapodsPlatform = "iphonesimulator", + cocoapodsArchs = "x86_64", + cocoapodsConfiguration = "Debug" + ) + ) + + nativeProjectWithCocoapodsAndIosAppPodFile(gradleVersion = gradleVersion, buildOptions = buildOptions) { + val syncTaskName = ":syncFramework" + buildGradleKts.addCocoapodsBlock( """ framework { @@ -177,9 +194,14 @@ class CocoaPodsIT : KGPBaseTest() { assertTasksUpToDate(podInstallTaskName) } + buildWithCocoapodsWrapper(syncTaskName) { + assertTasksExecuted(syncTaskName) + } + buildGradleKts.addFrameworkBlock("isStatic = true") buildWithCocoapodsWrapper(podImportTaskName) { assertTasksExecuted(dummyTaskName) + assertOutputContains("Regenerating dummy-framework because present framework has different linkage") assertTasksExecuted(podInstallTaskName) } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsXcodeIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsXcodeIT.kt index 3f237418d7a..97e2e398635 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsXcodeIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsXcodeIT.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.native import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.DUMMY_FRAMEWORK_TASK_NAME import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_INSTALL_TASK_NAME import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.gradle.util.assertProcessRunResult @@ -46,6 +47,19 @@ class CocoaPodsXcodeIT : KGPBaseTest() { mapOf("kotlin-library" to null) ) + @DisplayName("Checks xcodebuild for ios-app with a single framework with manual pod install") + @GradleTest + fun testXcodeUseFrameworksSingleWithManualPodInstall(gradleVersion: GradleVersion) { + nativeProject(cocoapodsSingleKtPod, gradleVersion, environmentVariables = environmentVariables) { + doTestXcode( + mode = ImportMode.FRAMEWORKS, + iosAppLocation = "ios-app", + subprojectsToFrameworkNamesMap = mapOf("kotlin-library" to null), + podInstall = ::manualPodInstall + ) + } + } + @DisplayName("Checks xcodebuild for ios-app with a single framework with custom name") @GradleTest fun testXcodeUseFrameworksWithCustomFrameworkNameSingle(gradleVersion: GradleVersion) = doTestXcode( @@ -66,7 +80,6 @@ class CocoaPodsXcodeIT : KGPBaseTest() { mapOf("kotlin-library" to null) ) - @DisplayName("Checks xcodebuild for ios-app using modular headers with a single framework with custom name") @GradleTest fun testXcodeUseModularHeadersWithCustomFrameworkNameSingle(gradleVersion: GradleVersion) = doTestXcode( @@ -77,6 +90,21 @@ class CocoaPodsXcodeIT : KGPBaseTest() { mapOf("kotlin-library" to "MultiplatformLibrary") ) + @DisplayName("Checks xcodebuild for ios-app with manual pod install and static framework") + @GradleTest + fun testXcodeWithManualPodInstallForStaticFramework(gradleVersion: GradleVersion) { + nativeProject(cocoapodsSingleKtPod, gradleVersion, environmentVariables = environmentVariables) { + buildGradleKts.addFrameworkBlock("isStatic = true") + + doTestXcode( + mode = ImportMode.MODULAR_HEADERS, + iosAppLocation = "ios-app", + subprojectsToFrameworkNamesMap = mapOf("kotlin-library" to "MultiplatformLibrary"), + podInstall = ::manualPodInstall + ) + } + } + @DisplayName("Checks xcodebuild for ios-app with kotlin library from root project") @GradleTest fun testXcodeBuildsWithKotlinLibraryFromRootProject(gradleVersion: GradleVersion) { @@ -163,6 +191,7 @@ class CocoaPodsXcodeIT : KGPBaseTest() { iosAppLocation: String?, subprojectsToFrameworkNamesMap: Map, arch: String = "x86_64", + podInstall: (taskPrefix: String, iosAppPath: Path) -> Unit = ::gradlePodInstall, ) { gradleProperties @@ -189,32 +218,46 @@ class CocoaPodsXcodeIT : KGPBaseTest() { // Generate podspec. build("$taskPrefix:podspec", buildOptions = buildOptions) - iosAppLocation?.also { + if (iosAppLocation != null) { // Set import mode for Podfile. - preparePodfile(it, mode) - // Install pods. - build("$taskPrefix:$POD_INSTALL_TASK_NAME", buildOptions = buildOptions) + preparePodfile(iosAppLocation, mode) - projectPath.resolve(it).apply { - // Run Xcode build. - val xcodebuildResult = runProcess( - cmd = listOf( - "xcodebuild", - "-sdk", "iphonesimulator", - "-configuration", "Release", - "-workspace", "$name.xcworkspace", - "-scheme", name, - "-arch", arch - ), - environmentVariables = environmentVariables.environmentalVariables, - workingDir = this.toFile(), - ) - assertProcessRunResult(xcodebuildResult) { - assertEquals(0, exitCode, "Exit code mismatch for `xcodebuild`.") - } + val iosAppPath = projectPath.resolve(iosAppLocation) + + // Install pods. + podInstall(taskPrefix, iosAppPath) + + // Run Xcode build. + val xcodebuildResult = runProcess( + cmd = listOf( + "xcodebuild", + "-sdk", "iphonesimulator", + "-configuration", "Release", + "-workspace", "${iosAppPath.name}.xcworkspace", + "-scheme", iosAppPath.name, + "-arch", arch + ), + environmentVariables = environmentVariables.environmentalVariables, + workingDir = iosAppPath.toFile(), + ) + assertProcessRunResult(xcodebuildResult) { + assertEquals(0, exitCode, "Exit code mismatch for `xcodebuild`.") } } } - } +} + +private fun TestProject.gradlePodInstall(taskPrefix: String, @Suppress("UNUSED_PARAMETER") iosAppPath: Path) { + build("$taskPrefix:$POD_INSTALL_TASK_NAME", buildOptions = buildOptions) +} + +private fun TestProject.manualPodInstall(taskPrefix: String, iosAppPath: Path) { + build("$taskPrefix:$DUMMY_FRAMEWORK_TASK_NAME", buildOptions = buildOptions) + + runProcess( + cmd = listOf("pod", "install"), + environmentVariables = environmentVariables.environmentalVariables, + workingDir = iosAppPath.toFile(), + ) } \ No newline at end of file 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 6b5fe2770cd..a61bc46a50b 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 @@ -10,6 +10,7 @@ import org.gradle.api.Action import org.gradle.api.Named import org.gradle.api.NamedDomainObjectSet import org.gradle.api.Project +import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency.PodLocation.* @@ -118,8 +119,8 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje anyFramework } - internal val podFrameworkName = anyPodFramework.map { it.baseName.asValidFrameworkName() } - internal val podFrameworkIsStatic = anyPodFramework.map { it.isStatic } + internal val podFrameworkName: Provider = anyPodFramework.map { it.baseName.asValidFrameworkName() } + internal val podFrameworkIsStatic: Provider = anyPodFramework.map { it.isStatic } /** * Configure custom Xcode Configurations to Native Build Types mapping 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 65379e97337..5fbe7826d92 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 @@ -60,8 +60,6 @@ internal class CocoapodsBuildDirs(private val layout: ProjectLayout) { get() = layout.buildDirectory.dir("cocoapods") val framework: Provider get() = dir("framework") - val dummyFramework: Provider - get() = dir("dummy.framework") val defs: Provider get() = dir("defs") val publish: Provider @@ -394,8 +392,9 @@ open class KotlinCocoapodsPlugin : Plugin { cocoapodsExtension: CocoapodsExtension ) { project.registerTask(DUMMY_FRAMEWORK_TASK_NAME) { task -> - task.frameworkName.set(cocoapodsExtension.podFrameworkName) - task.useStaticFramework.set(cocoapodsExtension.podFrameworkIsStatic) + task.frameworkName.convention(cocoapodsExtension.podFrameworkName) + task.useStaticFramework.convention(cocoapodsExtension.podFrameworkIsStatic) + task.outputFramework.convention(project.layout.cocoapodsBuildDirs.framework.map { it.dir(task.frameworkName.get() + ".framework") }) } } @@ -478,17 +477,18 @@ open class KotlinCocoapodsPlugin : Plugin { cocoapodsExtension: CocoapodsExtension ) { val podspecTaskProvider = project.tasks.named(POD_SPEC_TASK_NAME) - val dummyFrameworkTaskProvider = project.tasks.named(DUMMY_FRAMEWORK_TASK_NAME) + val dummyFrameworkTaskProvider = project.tasks.named(DUMMY_FRAMEWORK_TASK_NAME) project.registerTask(POD_INSTALL_TASK_NAME) { task -> task.group = TASK_GROUP task.description = "Invokes `pod install` call within Podfile location directory" task.podfile.set(project.provider { cocoapodsExtension.podfile }) task.podspec.set(podspecTaskProvider.map { it.outputFile }) + task.useStaticFramework.set(cocoapodsExtension.podFrameworkIsStatic) 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) + task.dependsOn(dummyFrameworkTaskProvider) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DummyFrameworkTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DummyFrameworkTask.kt index ab8595b0aab..bfdf3cea2ac 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DummyFrameworkTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DummyFrameworkTask.kt @@ -7,17 +7,16 @@ package org.jetbrains.kotlin.gradle.tasks import org.gradle.api.DefaultTask -import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.gradle.work.DisableCachingByDefault -import org.jetbrains.kotlin.gradle.plugin.cocoapods.cocoapodsBuildDirs -import org.jetbrains.kotlin.gradle.utils.mapToFile +import org.jetbrains.kotlin.gradle.utils.MachO +import org.jetbrains.kotlin.gradle.utils.getFile import java.io.File -import javax.inject.Inject /** * Creates a dummy framework in the target directory. @@ -31,7 +30,7 @@ import javax.inject.Inject * and then replace it with the real one during a real build process. */ @DisableCachingByDefault -abstract class DummyFrameworkTask @Inject constructor(projectLayout: ProjectLayout) : DefaultTask() { +abstract class DummyFrameworkTask : DefaultTask() { @get:Input abstract val frameworkName: Property @@ -40,13 +39,18 @@ abstract class DummyFrameworkTask @Inject constructor(projectLayout: ProjectLayo abstract val useStaticFramework: Property @get:OutputDirectory - val outputFramework: Provider = projectLayout.cocoapodsBuildDirs.dummyFramework.mapToFile() + abstract val outputFramework: DirectoryProperty + + @get:Internal + @Deprecated("Use outputFramework", replaceWith = ReplaceWith("outputFramework.get().asFile")) + val destinationDir: File + get() = outputFramework.getFile() + + private val linkageName: String + get() = if (useStaticFramework.get()) "static" else "dynamic" private val dummyFrameworkResource: String - get() { - val staticOrDynamic = if (!useStaticFramework.get()) "dynamic" else "static" - return "/cocoapods/$staticOrDynamic/dummy.framework/" - } + get() = "/cocoapods/$linkageName/dummy.framework/" private fun copyResource(from: String, to: File) { to.parentFile.mkdirs() @@ -71,23 +75,22 @@ abstract class DummyFrameworkTask @Inject constructor(projectLayout: ProjectLayo private fun copyFrameworkFile(relativeFrom: String, relativeTo: String = relativeFrom) = copyResource( "$dummyFrameworkResource$relativeFrom", - outputFramework.get().resolve(relativeTo) + outputFramework.getFile().resolve(relativeTo) ) private fun copyFrameworkTextFile( relativeFrom: String, relativeTo: String = relativeFrom, - transform: (String) -> String = { it } + transform: (String) -> String = { it }, ) = copyTextResource( "$dummyFrameworkResource$relativeFrom", - outputFramework.get().resolve(relativeTo), + outputFramework.getFile().resolve(relativeTo), transform ) - @TaskAction - fun create() { + private fun copyFramework() { // Reset the destination directory - with(outputFramework.get()) { + with(outputFramework.getFile()) { deleteRecursively() mkdirs() } @@ -104,4 +107,25 @@ abstract class DummyFrameworkTask @Inject constructor(projectLayout: ProjectLayo } } } + + + @TaskAction + fun create() { + val framework = outputFramework.getFile() + val binary = framework.resolve(frameworkName.get()) + + return when { + !binary.exists() -> { + logger.info("Generating dummy-framework because the framework is missing") + copyFramework() + } + MachO.isDylib(binary, logger) == !useStaticFramework.get() -> { + logger.info("Skipping dummy-framework generation because a $linkageName framework is already present") + } + else -> { + logger.info("Regenerating dummy-framework because present framework has different linkage") + copyFramework() + } + } + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt index 62e6604456d..9e351cc5371 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt @@ -7,20 +7,16 @@ package org.jetbrains.kotlin.gradle.targets.native.tasks -import org.gradle.api.file.ProjectLayout import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.SpecRepos -import org.jetbrains.kotlin.gradle.plugin.cocoapods.cocoapodsBuildDirs import org.jetbrains.kotlin.gradle.targets.native.cocoapods.MissingCocoapodsMessage import org.jetbrains.kotlin.gradle.targets.native.cocoapods.MissingSpecReposMessage import java.io.File -import javax.inject.Inject -abstract class PodInstallTask @Inject constructor(projectLayout: ProjectLayout) : AbstractPodInstallTask() { +abstract class PodInstallTask : AbstractPodInstallTask() { @get:Optional @get:InputFile @@ -29,38 +25,15 @@ abstract class PodInstallTask @Inject constructor(projectLayout: ProjectLayout) @get:Input abstract val frameworkName: Property + @get:Input + abstract val useStaticFramework: Property + @get:Nested abstract val specRepos: Property @get:Nested abstract val pods: ListProperty - @get:InputDirectory - abstract val dummyFramework: Property - - private val framework = projectLayout.cocoapodsBuildDirs.framework.map { it.file("${frameworkName.get()}.framework").asFile } - 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.rename(dest: Provider) = 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 } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/MachO.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/MachO.kt new file mode 100644 index 00000000000..50ac8b70b85 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/MachO.kt @@ -0,0 +1,59 @@ +/* + * 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.gradle.api.logging.Logger +import org.jetbrains.kotlin.resolve.constants.fromUIntToLong +import java.io.File +import java.io.IOException +import java.io.RandomAccessFile + +// https://github.com/aidansteele/osx-abi-macho-file-format-reference +@Suppress("SpellCheckingInspection") +internal object MachO { + + private const val FAT_MAGIC = 0xcafebabeL + private const val FAT_MAGIC_64 = 0xcafebabfL + + private const val MH_CIGAM = 0xcefaedfeL + private const val MH_CIGAM_64 = 0xcffaedfeL + + private const val MH_BILYD = 0x06000000L // MH_DYLIB = 0x6 converted for JVM + + private const val FILE_TYPE_OFFSET = 12L + private const val FAT_FIRST_MACHO_OFFSET_OFFSET = 16L + + /** + * Checks if the [file] is a Mach-O dynamic shared library + * or a Mach-O fat binary containing a number of dynamic libraries + */ + fun isDylib(file: File, logger: Logger): Boolean { + try { + RandomAccessFile(file, "r").use { raf -> + val magic = raf.readInt().fromUIntToLong() + + val fileTypeOffset = when (magic) { + // all supported platforms are little-endian and JVM is big-endian, so we don't check MH_MAGIC[_64] + MH_CIGAM, MH_CIGAM_64 -> FILE_TYPE_OFFSET + FAT_MAGIC, FAT_MAGIC_64 -> { + raf.seek(FAT_FIRST_MACHO_OFFSET_OFFSET) + val firstMachoOffset = raf.readInt().fromUIntToLong() // guaranteed to be big-endian by the spec + firstMachoOffset + FILE_TYPE_OFFSET + } + else -> return false // not Mach-O or fat + } + + raf.seek(fileTypeOffset) + val fileType = raf.readInt().toLong() + + return fileType == MH_BILYD + } + } catch (e: IOException) { + logger.info("IOException while cheking if '$file' is a dylib", e) + return false + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/MachOTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/MachOTest.kt new file mode 100644 index 00000000000..9288f40071e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/MachOTest.kt @@ -0,0 +1,67 @@ +/* + * 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. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.kotlin.gradle.unitTests + +import org.jetbrains.kotlin.gradle.util.buildProject +import org.jetbrains.kotlin.gradle.utils.MachO +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.File +import java.io.InputStream +import kotlin.test.assertEquals + +class MachOTest { + + @get:Rule + val temporaryFolder = TemporaryFolder() + + @Test + fun `dynamic dummy`() { + assertDylib(true, "/cocoapods/dynamic/dummy.framework/dummy") + } + + @Test + fun `static dummy`() { + assertDylib(false, "/cocoapods/static/dummy.framework/dummy") + } + + @Test + fun `dynamic fat`() { + assertDylib(true, "/machoBinaries/dynamicFat") + } + + @Test + fun `static fat`() { + assertDylib(false, "/machoBinaries/staticFat") + } + + @Test + fun `dynamic lib`() { + assertDylib(true, "/machoBinaries/dynamicLib") + } + + @Test + fun `static lib`() { + assertDylib(false, "/machoBinaries/staticLib") + } + + private fun assertDylib(expected: Boolean, resource: String) { + val tmp = temporaryFolder.newFile() + tmp.writeResource(resource) + return assertEquals(expected, MachO.isDylib(tmp, buildProject().logger)) + } + + private fun File.writeResource(resource: String) { + outputStream().use { out -> + MachOTest::class.java.getResourceAsStream(resource)!!.use { input: InputStream -> + input.copyTo(out) + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/dynamicFat b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/dynamicFat new file mode 100755 index 0000000000000000000000000000000000000000..f85e1ec7c492f536b4b094e5a0bb726f758c8657 GIT binary patch literal 24752 zcmeI3&ud&&6vywhseO&n&WMP=YF|Q9QYmd9F0xT3lE4(B7?WT(P7H6%{K;$4Pu1Oxi*~E$ zU7LLn9=5%J!nM3^BJm9{C)By6YULbmnPqCFTFWh*ec3@cg?Y4@35_qOZJYNfA_Rnh z5D)@FKnMr{As_^Vz=I_))3{Z?8*K6-7uHsXp&DKNo7{0V#_!cPf{WK%1Kc+iy71E) z;Q!s%3!MGSudP-q!~M;f@Xf_aG24qxg4u&S%L>HT+#hg1&HVyP9d!!p+}reE-(Yt1 z?ALImPZSc;CJ=nqUt`ZZa$}?!ZV2D_(;lgh+~{$ zjw@8kt9##2)2xTe+HuCWT^^G}gn$qb0zyCt2mv7=1cZPP5CTF#2nd1yMc{^s|4%SR zfHB{U|8FA7nDPIVr%(p5v5&{BM}zz^)7nkfW*j$j0%()6S583M#vhzC(-qrr;~(*I z1K4R|`>XBoxPLr@pt$R^&GQB)fwkwx_sSJ0SbI-f18noWqb=L~+dXf9c?%S)qC5uE zV}7^TM%P|B1TKCfV~wyKx8d8@I#dA9kE>?C|o80FG2KY@$f zdlJ(wN5J{IpL_-19PRpk@p<~sd9U1sr1ggpITW^id(J;IYW^O`xMLneWWdf0jc(dz zr@V$CjFGSJZ>PKln&|U#8|Z5gnzbCGgrPw^WHouxHuC^@KUbg!q5o@c!!p)y`Fn&d zipy=tHohAQe;dv>e!g+9=h3g<>Bm@vc9-0S5zL{F^mRYE4Vb1*q0nwR1Ow>Pw$1w# z5duO$2nYcoAOwVf5D)?nn!rq^aVt~5o6M}e$l(5_+GLnMV*4}ojpW7a>#c#$BNryt zPJDvO=u~T<0c~UaZ~yBU|1|btF2?_o-J|Ug;~#A+9*?e$J-~Z!sKjzIk?c7dtos`d zJMPr7wdvZTg@6zc0zyCt2mv7=1cZPP5CTF#2nd0LPaxHsWZ3`CasSDkX_R=@xN literal 0 HcmV?d00001 diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/dynamicLib b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/dynamicLib new file mode 100755 index 0000000000000000000000000000000000000000..301c06973a656476a2ec9d2aa47d620b0378e0f4 GIT binary patch literal 66696 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%0D3TQ+;CvWg_ks~YU+Q| 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 literal 0 HcmV?d00001 diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/staticFat b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/staticFat new file mode 100755 index 0000000000000000000000000000000000000000..97afba5ae5d4bafe0d20109c4866eda87c32fffe GIT binary patch literal 8968 zcmeHL&uddb5Z<)44K}qoC`1r>O2CUf=*eS?7T&>tNP@k{Hngb?Y1cJsYl=KFa3=B-j{ z2s#EGRw@mX*u#=a^kN>5l8iw`1OY)n5D)|e0YN|z5CjAPK|l}?1Ox#=;6Fp))6Z|m zZtz!__tgmW7B?PP0GJ)8DC>FO3s?{j zbec32tru2k53P&b)XhyOJU%TDzjSpt_hiSKVGY{YSrAnoK z-Fr}PR2Hw;$6iG~?w>fX)NB4$$Sb~+Z<^!+A?9uiz^cV^P3aY27A)CgSp)$=KoAfF z1OY)n5D)|e0YN|z5CjB)9s-}k`9Hz=o3kY6aXTD)+$ePs`m8r(+ECx}(s`a6yd$5p zDb@CaJq;&-G&RKq!hWpXfwiq;+yKx2#t%e&$PrL-?tP)>dPWW``qir|#f3nx)gG5? zi(22k6FXMZ&hea`M?Yfr1=hjScZ1fYWxdoWR!d>QN&aHq^_h0I>H0aBoWydpr_DkK zaT$B4N6!$&; z_$PtiurmDQzDlg86Y11g@;S2jdoMwoU`i;__f#|I(i=m!Cq~0En}hTzawCA?0>kh} Rc~QiZFxJPFFmQq|`2`wQiedl& literal 0 HcmV?d00001 diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/staticLib b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/machoBinaries/staticLib new file mode 100644 index 0000000000000000000000000000000000000000..1dd1bd88248018a49bc8905756e8ed1ad3a3c867 GIT binary patch literal 11480 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 literal 0 HcmV?d00001