Refactor FatFrameworkTask: use FrameworkDescriptor instead Framework.

for independence from module compilation.
This commit is contained in:
Konstantin Tskhovrebov
2021-10-21 16:04:15 +03:00
committed by Space
parent c3c79b874d
commit a9f850dac0
2 changed files with 71 additions and 50 deletions
@@ -85,7 +85,7 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : AbstractKotl
project.afterEvaluate { project.afterEvaluate {
val task = taskProvider.get() val task = taskProvider.get()
val artifactFile = when (task) { val artifactFile = when (task) {
is FatFrameworkTask -> task.fatFrameworkDir is FatFrameworkTask -> task.fatFramework
else -> binary.outputFile else -> binary.outputFile
} }
val linkArtifact = project.artifacts.add(name, artifactFile) { artifact -> val linkArtifact = project.artifacts.add(name, artifactFile) { artifact ->
@@ -12,20 +12,22 @@ import org.gradle.api.tasks.*
import org.jetbrains.kotlin.gradle.plugin.cocoapods.asValidFrameworkName import org.jetbrains.kotlin.gradle.plugin.cocoapods.asValidFrameworkName
import org.jetbrains.kotlin.gradle.plugin.mpp.Framework import org.jetbrains.kotlin.gradle.plugin.mpp.Framework
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget 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.gradle.utils.appendLine
import org.jetbrains.kotlin.konan.target.Architecture import org.jetbrains.kotlin.konan.target.Architecture
import org.jetbrains.kotlin.konan.target.Family 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.target.KonanTarget.*
import org.jetbrains.kotlin.konan.util.visibleName import org.jetbrains.kotlin.konan.util.visibleName
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
private val Framework.files: IosFrameworkFiles class FrameworkDsymLayout(val rootDir: File) {
get() = IosFrameworkFiles(outputFile) init {
require(rootDir.extension == "framework.dSYM")
}
private class IosDsymFiles(val rootDir: File) { private val frameworkName = rootDir.name.removeSuffix(".framework.dSYM")
val frameworkName = rootDir.name.removeSuffix(".framework.dSYM")
val binaryDir = rootDir.resolve("Contents/Resources/DWARF") val binaryDir = rootDir.resolve("Contents/Resources/DWARF")
val binary = binaryDir.resolve(frameworkName) val binary = binaryDir.resolve(frameworkName)
@@ -35,15 +37,15 @@ private class IosDsymFiles(val rootDir: File) {
binaryDir.mkdirs() 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): private val frameworkName = rootDir.nameWithoutExtension
this(parentDir.resolve("$frameworkName.framework"))
val frameworkName: String = rootDir.nameWithoutExtension
val headerDir = rootDir.resolve("Headers") val headerDir = rootDir.resolve("Headers")
val modulesDir = rootDir.resolve("Modules") val modulesDir = rootDir.resolve("Modules")
@@ -53,26 +55,48 @@ private class IosFrameworkFiles(val rootDir: File) {
val moduleFile = modulesDir.resolve("module.modulemap") val moduleFile = modulesDir.resolve("module.modulemap")
val infoPlist = rootDir.resolve("Info.plist") 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() { fun mkdirs() {
rootDir.mkdirs() rootDir.mkdirs()
headerDir.mkdir() headerDir.mkdir()
modulesDir.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. * Task running lipo to create a fat framework from several simple frameworks. It also merges headers, plists and module files.
*/ */
open class FatFrameworkTask : DefaultTask() { open class FatFrameworkTask : DefaultTask() {
private val archToFramework: MutableMap<Architecture, FrameworkDescriptor> = mutableMapOf()
//region DSL properties. //region DSL properties.
/** /**
* A collection of frameworks used ot build the fat framework. * A collection of frameworks used ot build the fat framework.
*/ */
@get:Internal // We take it into account as an input in the inputFrameworkFiles property. @get:Internal // We take it into account as an input in the inputFrameworkFiles property.
val frameworks: Collection<Framework> val frameworks: Collection<FrameworkDescriptor>
get() = archToFramework.values get() = archToFramework.values
/** /**
@@ -87,33 +111,23 @@ open class FatFrameworkTask : DefaultTask() {
@OutputDirectory @OutputDirectory
var destinationDir: File = project.buildDir.resolve("fat-framework") 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<Architecture, Framework> = mutableMapOf()
@get:Internal @get:Internal
val fatFrameworkName: String val fatFrameworkName: String
get() = baseName.asValidFrameworkName() get() = baseName.asValidFrameworkName()
private val fatFramework: IosFrameworkFiles @get:Internal
get() = IosFrameworkFiles(destinationDir, fatFrameworkName) val fatFramework: File
get() = destinationDir.resolve(fatFrameworkName + ".framework")
private val fatDsym: IosDsymFiles private val fatFrameworkLayout: FrameworkLayout
get() = fatFramework.dSYM get() = FrameworkLayout(fatFramework)
@get:PathSensitive(PathSensitivity.ABSOLUTE) @get:PathSensitive(PathSensitivity.ABSOLUTE)
@get:IgnoreEmptyDirectories @get:IgnoreEmptyDirectories
@get:InputFiles @get:InputFiles
@get:SkipWhenEmpty @get:SkipWhenEmpty
protected val inputFrameworkFiles: Iterable<FileTree> protected val inputFrameworkFiles: Iterable<FileTree>
get() = frameworks.map { project.fileTree(it.outputFile) } get() = frameworks.map { project.fileTree(it.file) }
@get:PathSensitive(PathSensitivity.ABSOLUTE) @get:PathSensitive(PathSensitivity.ABSOLUTE)
@get:IgnoreEmptyDirectories @get:IgnoreEmptyDirectories
@@ -137,9 +151,17 @@ open class FatFrameworkTask : DefaultTask() {
* Adds the specified frameworks in this fat framework. * Adds the specified frameworks in this fat framework.
*/ */
fun from(frameworks: Iterable<Framework>) { fun from(frameworks: Iterable<Framework>) {
fromFrameworkDescriptors(frameworks.map { FrameworkDescriptor(it) })
frameworks.forEach { dependsOn(it.linkTask) }
}
/**
* Adds the specified frameworks in this fat framework.
*/
fun fromFrameworkDescriptors(frameworks: Iterable<FrameworkDescriptor>) {
frameworks.forEach { framework -> frameworks.forEach { framework ->
val arch = framework.konanTarget.architecture val arch = framework.target.architecture
val family = framework.konanTarget.family val family = framework.target.family
val fatFrameworkFamily = getFatFrameworkFamily() val fatFrameworkFamily = getFatFrameworkFamily()
require(fatFrameworkFamily == null || family == fatFrameworkFamily) { require(fatFrameworkFamily == null || family == fatFrameworkFamily) {
"Cannot add a binary with platform family '${family.visibleName}' to the fat framework:\n" + "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 }) { 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 { buildString {
append("Cannot create a fat framework from:\n") append("Cannot create a fat framework from:\n")
archToFramework.forEach { append("${it.value.baseName} - ${it.key.name.toLowerCase()} - ${it.value.staticOrDynamic()}\n") } archToFramework.forEach { append("${it.value.name} - ${it.key.name.toLowerCase()} - ${staticName(it.value.isStatic)}\n") }
append("${framework.baseName} - ${arch.name.toLowerCase()} - ${framework.staticOrDynamic()}\n") append("${framework.name} - ${arch.name.toLowerCase()} - ${staticName(framework.isStatic)}\n")
append("All input frameworks must be either static or dynamic") append("All input frameworks must be either static or dynamic")
} }
} }
archToFramework[arch] = framework archToFramework[arch] = framework
dependsOn(framework.linkTask)
} }
} }
// endregion. // endregion.
private fun getFatFrameworkFamily(): Family? { private fun getFatFrameworkFamily(): Family? {
assert(archToFramework.values.distinctBy { it.konanTarget.family }.size <= 1) assert(archToFramework.values.distinctBy { it.target.family }.size <= 1)
return archToFramework.values.firstOrNull()?.konanTarget?.family return archToFramework.values.firstOrNull()?.target?.family
} }
private val Architecture.clangMacro: String private val Architecture.clangMacro: String
get() = when(this) { get() = when (this) {
Architecture.X86 -> "__i386__" Architecture.X86 -> "__i386__"
Architecture.X64 -> "__x86_64__" Architecture.X64 -> "__x86_64__"
Architecture.ARM32 -> "__arm__" Architecture.ARM32 -> "__arm__"
@@ -185,12 +205,12 @@ open class FatFrameworkTask : DefaultTask() {
else -> error("Fat frameworks are not supported for architecture `$name`") else -> error("Fat frameworks are not supported for architecture `$name`")
} }
private val Framework.plistPlatform: String private val FrameworkDescriptor.plistPlatform: String
get() = when (konanTarget) { get() = when (target) {
IOS_ARM32, IOS_ARM64, IOS_X64, IOS_SIMULATOR_ARM64 -> "iPhoneOS" IOS_ARM32, IOS_ARM64, IOS_X64, IOS_SIMULATOR_ARM64 -> "iPhoneOS"
TVOS_ARM64, TVOS_X64, TVOS_SIMULATOR_ARM64 -> "AppleTVOS" TVOS_ARM64, TVOS_X64, TVOS_SIMULATOR_ARM64 -> "AppleTVOS"
WATCHOS_ARM32, WATCHOS_ARM64, WATCHOS_X86, WATCHOS_X64, WATCHOS_SIMULATOR_ARM64 -> "WatchOS" 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. // 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) 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) runInstallNameTool(outputFile, fatFrameworkName)
} }
} }
@@ -344,6 +364,7 @@ open class FatFrameworkTask : DefaultTask() {
return return
} }
val fatDsym = fatFrameworkLayout.dSYM
fatDsym.mkdirs() fatDsym.mkdirs()
// Merge dSYM binary. // Merge dSYM binary.
@@ -360,13 +381,13 @@ open class FatFrameworkTask : DefaultTask() {
@TaskAction @TaskAction
protected fun createFatFramework() { protected fun createFatFramework() {
fatFramework.mkdirs() val outFramework = fatFrameworkLayout
val frameworkName = fatFramework.frameworkName outFramework.mkdirs()
mergeBinaries(fatFramework.binary) mergeBinaries(outFramework.binary)
mergeHeaders(fatFramework.header) mergeHeaders(outFramework.header)
createModuleFile(fatFramework.moduleFile, frameworkName) createModuleFile(outFramework.moduleFile, fatFrameworkName)
mergePlists(fatFramework.infoPlist, frameworkName) mergePlists(outFramework.infoPlist, fatFrameworkName)
mergeDSYM() mergeDSYM()
} }