[KT-47355] Support macOS targets in FatFramework task.

This commit is contained in:
konstantin.tskhovrebov
2022-08-04 17:37:56 +02:00
committed by Space
parent 09a72fd679
commit d5164fbc86
2 changed files with 74 additions and 22 deletions
@@ -7,12 +7,14 @@ package org.jetbrains.kotlin.gradle.native
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.gradle.embedProject
import org.jetbrains.kotlin.gradle.tasks.FrameworkLayout
import org.jetbrains.kotlin.gradle.transformProjectWithPluginsDsl
import org.jetbrains.kotlin.gradle.util.*
import org.jetbrains.kotlin.konan.target.HostManager
import org.junit.Assume
import org.junit.BeforeClass
import org.junit.Test
import java.io.File
import java.util.*
import kotlin.test.assertTrue
@@ -71,10 +73,39 @@ class FatFrameworkIT : BaseGradleIT() {
}
}
@Test
fun smokeMacos() {
with(transformProjectWithPluginsDsl("smoke", directoryPrefix = "native-fat-framework")) {
gradleBuildScript().modify {
it.checkedReplace("iosArm32()", "")
.checkedReplace("iosArm64()", "macosArm64()")
.checkedReplace("iosX64()", "macosX64()")
}
build("fat") {
checkSmokeBuild(
archs = listOf("x64", "arm64"),
targetPrefix = "macos",
expectedPlistPlatform = "MacOSX",
true
)
val binary = fileInWorkingDir("build/fat-framework/smoke.framework/Versions/A/smoke")
with(runProcess(listOf("file", binary.absolutePath), projectDir)) {
assertTrue(isSuccessful)
assertTrue(output.contains("\\(for architecture x86_64\\):\\s+Mach-O 64-bit dynamically linked shared library x86_64".toRegex()))
assertTrue(output.contains("\\(for architecture arm64\\):\\s+Mach-O 64-bit dynamically linked shared library arm64".toRegex()))
}
}
}
}
private fun CompiledProject.checkSmokeBuild(
archs: List<String>,
targetPrefix: String,
expectedPlistPlatform: String
expectedPlistPlatform: String,
isMacosFramework: Boolean = false
) {
assertSuccessful()
val linkTasks = archs.map {
@@ -89,17 +120,21 @@ class FatFrameworkIT : BaseGradleIT() {
assertTasksExecuted(linkTasks)
assertTasksExecuted(":fat")
assertFileExists("build/fat-framework/smoke.framework/smoke")
assertFileExists("build/fat-framework/smoke.framework/Headers/smoke.h")
assertFileExists("build/fat-framework/smoke.framework.dSYM/Contents/Resources/DWARF/smoke")
val frameworkLayout = FrameworkLayout(fileInWorkingDir("build/fat-framework/smoke.framework"), isMacosFramework)
val headerContent = fileInWorkingDir("build/fat-framework/smoke.framework/Headers/smoke.h").readText()
fun File.projectRelative() = relativeTo(File(workingDir, project.projectName)).path
assertFileExists(frameworkLayout.binary.projectRelative())
assertFileExists(frameworkLayout.header.projectRelative())
assertFileExists(frameworkLayout.dSYM.binary.projectRelative())
val headerContent = frameworkLayout.header.readText()
assertTrue(
headerContent.contains("+ (int32_t)foo __attribute__((swift_name(\"foo()\")));"),
"Unexpected header content:\n$headerContent"
)
val plistContent = fileInWorkingDir("build/fat-framework/smoke.framework/Info.plist")
val plistContent = frameworkLayout.infoPlist
.readLines()
.joinToString(separator = "\n") { it.trim() }
@@ -20,8 +20,11 @@ import org.jetbrains.kotlin.konan.target.HostManager
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.utils.PathUtil
import java.io.ByteArrayOutputStream
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
class FrameworkDsymLayout(val rootDir: File) {
init {
@@ -41,27 +44,43 @@ class FrameworkDsymLayout(val rootDir: File) {
fun exists() = rootDir.exists()
}
class FrameworkLayout(val rootDir: File) {
class FrameworkLayout(
val rootDir: File,
val isMacosFramework: Boolean
) {
init {
require(rootDir.extension == "framework")
}
private val frameworkName = rootDir.nameWithoutExtension
private val macosVersionsDir = rootDir.resolve("Versions")
private val macosADir = macosVersionsDir.resolve("A")
private val macosResourcesDir = macosADir.resolve("Resources")
private val contentDir = if (isMacosFramework) macosADir else rootDir
val headerDir = rootDir.resolve("Headers")
val modulesDir = rootDir.resolve("Modules")
val headerDir = contentDir.resolve("Headers")
val modulesDir = contentDir.resolve("Modules")
val binary = rootDir.resolve(frameworkName)
val binary = contentDir.resolve(frameworkName)
val header = headerDir.resolve("$frameworkName.h")
val moduleFile = modulesDir.resolve("module.modulemap")
val infoPlist = rootDir.resolve("Info.plist")
val infoPlist = (if (isMacosFramework) macosResourcesDir else rootDir).resolve("Info.plist")
val dSYM = FrameworkDsymLayout(rootDir.parentFile.resolve("$frameworkName.framework.dSYM"))
fun mkdirs() {
rootDir.mkdirs()
headerDir.mkdir()
modulesDir.mkdir()
headerDir.mkdirs()
modulesDir.mkdirs()
if (isMacosFramework) {
macosResourcesDir.mkdirs()
val root = rootDir.toPath()
Files.createSymbolicLink(root.resolve("Headers"), headerDir.toPath())
Files.createSymbolicLink(root.resolve("Modules"), modulesDir.toPath())
Files.createSymbolicLink(root.resolve("Resources"), macosResourcesDir.toPath())
Files.createSymbolicLink(root.resolve(frameworkName), binary.toPath())
Files.createSymbolicLink(macosVersionsDir.toPath().resolve("Current"), macosADir.toPath())
}
}
fun exists() = rootDir.exists()
@@ -83,7 +102,7 @@ class FrameworkDescriptor(
}
val name = file.nameWithoutExtension
val files = FrameworkLayout(file)
val files = FrameworkLayout(file, target.family == Family.OSX)
}
/**
@@ -124,9 +143,6 @@ open class FatFrameworkTask : DefaultTask() {
val fatFramework: File
get() = destinationDir.resolve(fatFrameworkName + ".framework")
private val fatFrameworkLayout: FrameworkLayout
get() = FrameworkLayout(fatFramework)
@get:PathSensitive(PathSensitivity.ABSOLUTE)
@get:IgnoreEmptyDirectories
@get:InputFiles
@@ -212,6 +228,7 @@ open class FatFrameworkTask : DefaultTask() {
private val FrameworkDescriptor.plistPlatform: String
get() = when (target) {
MACOS_X64, MACOS_ARM64 -> "MacOSX"
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"
@@ -358,7 +375,7 @@ open class FatFrameworkTask : DefaultTask() {
}
}
private fun mergeDSYM() {
private fun mergeDSYM(fatDsym: FrameworkDsymLayout) {
val dsymInputs = archToFramework.mapValues { (_, framework) ->
framework.files.dSYM
}.filterValues {
@@ -369,7 +386,6 @@ open class FatFrameworkTask : DefaultTask() {
return
}
val fatDsym = fatFrameworkLayout.dSYM
fatDsym.mkdirs()
// Merge dSYM binary.
@@ -386,21 +402,22 @@ open class FatFrameworkTask : DefaultTask() {
@TaskAction
protected fun createFatFramework() {
val outFramework = fatFrameworkLayout
val outFramework = FrameworkLayout(fatFramework, getFatFrameworkFamily() == Family.OSX)
outFramework.mkdirs()
mergeBinaries(outFramework.binary)
mergeHeaders(outFramework.header)
createModuleFile(outFramework.moduleFile, fatFrameworkName)
mergePlists(outFramework.infoPlist, fatFrameworkName)
mergeDSYM()
mergeDSYM(outFramework.dSYM)
}
companion object {
private val supportedTargets = listOf(
IOS_ARM32, IOS_ARM64, IOS_X64,
WATCHOS_ARM32, WATCHOS_ARM64, WATCHOS_X86, WATCHOS_X64,
TVOS_ARM64, TVOS_X64
TVOS_ARM64, TVOS_X64,
MACOS_X64, MACOS_ARM64
)
fun isSupportedTarget(target: KotlinNativeTarget): Boolean {