diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt index 218668c0447..0b12dd88b81 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt @@ -85,7 +85,7 @@ open class KotlinNativeTargetConfigurator : AbstractKotl project.afterEvaluate { val task = taskProvider.get() val artifactFile = when (task) { - is FatFrameworkTask -> task.fatFrameworkDir + is FatFrameworkTask -> task.fatFramework else -> binary.outputFile } val linkArtifact = project.artifacts.add(name, artifactFile) { artifact -> diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/FatFrameworkTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/FatFrameworkTask.kt index 0e86d99f375..fd18f40c7bd 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/FatFrameworkTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/FatFrameworkTask.kt @@ -12,20 +12,22 @@ import org.gradle.api.tasks.* import org.jetbrains.kotlin.gradle.plugin.cocoapods.asValidFrameworkName import org.jetbrains.kotlin.gradle.plugin.mpp.Framework import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind import org.jetbrains.kotlin.gradle.utils.appendLine import org.jetbrains.kotlin.konan.target.Architecture import org.jetbrains.kotlin.konan.target.Family +import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget.* import org.jetbrains.kotlin.konan.util.visibleName import java.io.ByteArrayOutputStream import java.io.File -private val Framework.files: IosFrameworkFiles - get() = IosFrameworkFiles(outputFile) +class FrameworkDsymLayout(val rootDir: File) { + init { + require(rootDir.extension == "framework.dSYM") + } -private class IosDsymFiles(val rootDir: File) { - - val frameworkName = rootDir.name.removeSuffix(".framework.dSYM") + private val frameworkName = rootDir.name.removeSuffix(".framework.dSYM") val binaryDir = rootDir.resolve("Contents/Resources/DWARF") val binary = binaryDir.resolve(frameworkName) @@ -35,15 +37,15 @@ private class IosDsymFiles(val rootDir: File) { binaryDir.mkdirs() } - fun exists(): Boolean = rootDir.exists() + fun exists() = rootDir.exists() } -private class IosFrameworkFiles(val rootDir: File) { +class FrameworkLayout(val rootDir: File) { + init { + require(rootDir.extension == "framework") + } - constructor(parentDir: File, frameworkName: String): - this(parentDir.resolve("$frameworkName.framework")) - - val frameworkName: String = rootDir.nameWithoutExtension + private val frameworkName = rootDir.nameWithoutExtension val headerDir = rootDir.resolve("Headers") val modulesDir = rootDir.resolve("Modules") @@ -53,26 +55,48 @@ private class IosFrameworkFiles(val rootDir: File) { val moduleFile = modulesDir.resolve("module.modulemap") val infoPlist = rootDir.resolve("Info.plist") - val dSYM: IosDsymFiles = IosDsymFiles(rootDir.parentFile.resolve("$frameworkName.framework.dSYM")) + val dSYM = FrameworkDsymLayout(rootDir.parentFile.resolve("$frameworkName.framework.dSYM")) fun mkdirs() { rootDir.mkdirs() headerDir.mkdir() modulesDir.mkdir() } + + fun exists() = rootDir.exists() +} + +class FrameworkDescriptor( + val file: File, + val isStatic: Boolean, + val target: KonanTarget +) { + constructor(framework: Framework) : this( + framework.outputFile, + framework.isStatic, + framework.konanTarget + ) + + init { + require(NativeOutputKind.FRAMEWORK.availableFor(target)) + } + + val name = file.nameWithoutExtension + val files = FrameworkLayout(file) } /** * Task running lipo to create a fat framework from several simple frameworks. It also merges headers, plists and module files. */ open class FatFrameworkTask : DefaultTask() { + private val archToFramework: MutableMap = mutableMapOf() //region DSL properties. /** * A collection of frameworks used ot build the fat framework. */ @get:Internal // We take it into account as an input in the inputFrameworkFiles property. - val frameworks: Collection + val frameworks: Collection get() = archToFramework.values /** @@ -87,33 +111,23 @@ open class FatFrameworkTask : DefaultTask() { @OutputDirectory var destinationDir: File = project.buildDir.resolve("fat-framework") - @get:Internal // We take it into account as an input in the destinationDir property. - val fatFrameworkDir: File - get() = fatFramework.rootDir - - @get:Internal // We take it into account as an input in the destinationDir property. - val fatDsymDir: File - get() = fatDsym.rootDir - // endregion. - - private val archToFramework: MutableMap = mutableMapOf() - @get:Internal val fatFrameworkName: String get() = baseName.asValidFrameworkName() - private val fatFramework: IosFrameworkFiles - get() = IosFrameworkFiles(destinationDir, fatFrameworkName) + @get:Internal + val fatFramework: File + get() = destinationDir.resolve(fatFrameworkName + ".framework") - private val fatDsym: IosDsymFiles - get() = fatFramework.dSYM + private val fatFrameworkLayout: FrameworkLayout + get() = FrameworkLayout(fatFramework) @get:PathSensitive(PathSensitivity.ABSOLUTE) @get:IgnoreEmptyDirectories @get:InputFiles @get:SkipWhenEmpty protected val inputFrameworkFiles: Iterable - get() = frameworks.map { project.fileTree(it.outputFile) } + get() = frameworks.map { project.fileTree(it.file) } @get:PathSensitive(PathSensitivity.ABSOLUTE) @get:IgnoreEmptyDirectories @@ -137,9 +151,17 @@ open class FatFrameworkTask : DefaultTask() { * Adds the specified frameworks in this fat framework. */ fun from(frameworks: Iterable) { + fromFrameworkDescriptors(frameworks.map { FrameworkDescriptor(it) }) + frameworks.forEach { dependsOn(it.linkTask) } + } + + /** + * Adds the specified frameworks in this fat framework. + */ + fun fromFrameworkDescriptors(frameworks: Iterable) { frameworks.forEach { framework -> - val arch = framework.konanTarget.architecture - val family = framework.konanTarget.family + val arch = framework.target.architecture + val family = framework.target.family val fatFrameworkFamily = getFatFrameworkFamily() require(fatFrameworkFamily == null || family == fatFrameworkFamily) { "Cannot add a binary with platform family '${family.visibleName}' to the fat framework:\n" + @@ -154,30 +176,28 @@ open class FatFrameworkTask : DefaultTask() { } require(archToFramework.all { it.value.isStatic == framework.isStatic }) { - fun Framework.staticOrDynamic(): String = if (isStatic) "static" else "dynamic" + fun staticName(isStatic: Boolean) = if (isStatic) "static" else "dynamic" buildString { append("Cannot create a fat framework from:\n") - archToFramework.forEach { append("${it.value.baseName} - ${it.key.name.toLowerCase()} - ${it.value.staticOrDynamic()}\n") } - append("${framework.baseName} - ${arch.name.toLowerCase()} - ${framework.staticOrDynamic()}\n") + archToFramework.forEach { append("${it.value.name} - ${it.key.name.toLowerCase()} - ${staticName(it.value.isStatic)}\n") } + append("${framework.name} - ${arch.name.toLowerCase()} - ${staticName(framework.isStatic)}\n") append("All input frameworks must be either static or dynamic") } } archToFramework[arch] = framework - dependsOn(framework.linkTask) - } } // endregion. private fun getFatFrameworkFamily(): Family? { - assert(archToFramework.values.distinctBy { it.konanTarget.family }.size <= 1) - return archToFramework.values.firstOrNull()?.konanTarget?.family + assert(archToFramework.values.distinctBy { it.target.family }.size <= 1) + return archToFramework.values.firstOrNull()?.target?.family } private val Architecture.clangMacro: String - get() = when(this) { + get() = when (this) { Architecture.X86 -> "__i386__" Architecture.X64 -> "__x86_64__" Architecture.ARM32 -> "__arm__" @@ -185,12 +205,12 @@ open class FatFrameworkTask : DefaultTask() { else -> error("Fat frameworks are not supported for architecture `$name`") } - private val Framework.plistPlatform: String - get() = when (konanTarget) { + private val FrameworkDescriptor.plistPlatform: String + get() = when (target) { IOS_ARM32, IOS_ARM64, IOS_X64, IOS_SIMULATOR_ARM64 -> "iPhoneOS" TVOS_ARM64, TVOS_X64, TVOS_SIMULATOR_ARM64 -> "AppleTVOS" WATCHOS_ARM32, WATCHOS_ARM64, WATCHOS_X86, WATCHOS_X64, WATCHOS_SIMULATOR_ARM64 -> "WatchOS" - else -> error("Fat frameworks are not supported for platform `${konanTarget.visibleName}`") + else -> error("Fat frameworks are not supported for platform `${target.visibleName}`") } // Runs the PlistBuddy utility with the given commands to configure the given plist file. @@ -248,7 +268,7 @@ open class FatFrameworkTask : DefaultTask() { runLipo(archToFramework.values.map { it.files.binary }, outputFile) - if (archToFramework.values.any{ it.isStatic.not() && it.baseName != fatFrameworkName }) { + if (archToFramework.values.any { !it.isStatic && it.name != fatFrameworkName }) { runInstallNameTool(outputFile, fatFrameworkName) } } @@ -344,6 +364,7 @@ open class FatFrameworkTask : DefaultTask() { return } + val fatDsym = fatFrameworkLayout.dSYM fatDsym.mkdirs() // Merge dSYM binary. @@ -360,13 +381,13 @@ open class FatFrameworkTask : DefaultTask() { @TaskAction protected fun createFatFramework() { - fatFramework.mkdirs() + val outFramework = fatFrameworkLayout - val frameworkName = fatFramework.frameworkName - mergeBinaries(fatFramework.binary) - mergeHeaders(fatFramework.header) - createModuleFile(fatFramework.moduleFile, frameworkName) - mergePlists(fatFramework.infoPlist, frameworkName) + outFramework.mkdirs() + mergeBinaries(outFramework.binary) + mergeHeaders(outFramework.header) + createModuleFile(outFramework.moduleFile, fatFrameworkName) + mergePlists(outFramework.infoPlist, fatFrameworkName) mergeDSYM() }