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 {
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 ->
@@ -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<Architecture, FrameworkDescriptor> = 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<Framework>
val frameworks: Collection<FrameworkDescriptor>
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<Architecture, Framework> = 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<FileTree>
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<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 ->
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()
}