Preserve symlinks during framework copy
This commit is contained in:
+26
@@ -78,6 +78,32 @@ class AppleFrameworkIT : BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that macOS framework has symlinks`() {
|
||||
with(Project("sharedAppleFramework")) {
|
||||
val options: BuildOptions = defaultBuildOptions().copy(
|
||||
customEnvironmentVariables = mapOf(
|
||||
"CONFIGURATION" to "debug",
|
||||
"SDK_NAME" to "macosx",
|
||||
"ARCHS" to "x86_64",
|
||||
"EXPANDED_CODE_SIGN_IDENTITY" to "-",
|
||||
"TARGET_BUILD_DIR" to workingDir.absolutePath,
|
||||
"FRAMEWORKS_FOLDER_PATH" to "${projectName}/build/xcode-derived"
|
||||
)
|
||||
)
|
||||
build(":shared:embedAndSignAppleFrameworkForXcode", options = options) {
|
||||
assertSuccessful()
|
||||
assertTasksExecuted(":shared:assembleDebugAppleFrameworkForXcodeMacosX64")
|
||||
// Verify symlinks in gradle build folder
|
||||
assertFileExists("/shared/build/xcode-frameworks/debug/macosx/sdk.framework/Headers")
|
||||
assertFileIsSymlink("/shared/build/xcode-frameworks/debug/macosx/sdk.framework/Headers")
|
||||
// Verify symlinks in xcode derived folder
|
||||
assertFileExists("/build/xcode-derived/sdk.framework/Headers")
|
||||
assertFileIsSymlink("/build/xcode-derived/sdk.framework/Headers")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check embedAndSignAppleFrameworkForXcode fail`() {
|
||||
with(Project("sharedAppleFramework")) {
|
||||
|
||||
+6
@@ -31,6 +31,7 @@ import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.test.*
|
||||
@@ -559,6 +560,11 @@ abstract class BaseGradleIT {
|
||||
return this
|
||||
}
|
||||
|
||||
fun CompiledProject.assertFileIsSymlink(path: String = ""): CompiledProject {
|
||||
assertTrue(Files.isSymbolicLink(fileInWorkingDir(path).toPath()), "The file [$path] isn't a symlink.")
|
||||
return this
|
||||
}
|
||||
|
||||
fun CompiledProject.assertNoSuchFile(path: String = ""): CompiledProject {
|
||||
assertFalse(fileInWorkingDir(path).exists(), "The file [$path] exists.")
|
||||
return this
|
||||
|
||||
+2
-1
@@ -6,10 +6,11 @@ plugins {
|
||||
kotlin {
|
||||
android()
|
||||
|
||||
val macosX64 = macosX64()
|
||||
val iosX64 = iosX64()
|
||||
val iosSimulatorArm64 = iosSimulatorArm64()
|
||||
val iosArm64 = iosArm64()
|
||||
configure(listOf(iosX64, iosSimulatorArm64, iosArm64)) {
|
||||
configure(listOf(macosX64, iosX64, iosSimulatorArm64, iosArm64)) {
|
||||
binaries {
|
||||
framework {
|
||||
baseName = "sdk"
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.github.jetbrains.myapplication
|
||||
|
||||
actual class Platform actual constructor() {
|
||||
actual val platform: String = "macOS"
|
||||
}
|
||||
+50
-17
@@ -6,10 +6,18 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin.mpp.apple
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.tasks.Copy
|
||||
import org.gradle.api.tasks.IgnoreEmptyDirectories
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.SkipWhenEmpty
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.Framework
|
||||
@@ -17,7 +25,6 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.enabledOnCurrentHost
|
||||
import org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
@@ -117,12 +124,12 @@ private fun Project.registerAssembleAppleFrameworkTask(framework: Framework): Ta
|
||||
}.also {
|
||||
it.configure { task -> task.from(framework) }
|
||||
}
|
||||
else -> registerTask<Copy>(frameworkTaskName) { task ->
|
||||
else -> registerTask<FrameworkCopy>(frameworkTaskName) { task ->
|
||||
task.description = "Packs $frameworkBuildType ${frameworkTarget.name} framework for Xcode"
|
||||
task.isEnabled = frameworkBuildType == envBuildType
|
||||
task.dependsOn(framework.linkTaskName)
|
||||
task.from(framework.outputDirectory)
|
||||
task.into(appleFrameworkDir(envFrameworkSearchDir))
|
||||
task.files = files({ framework.outputDirectory.listFiles() })
|
||||
task.destDir = appleFrameworkDir(envFrameworkSearchDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,7 +167,7 @@ internal fun Project.registerEmbedAndSignAppleFrameworkTask(framework: Framework
|
||||
return
|
||||
}
|
||||
|
||||
val embedAndSignTask = locateOrRegisterTask<Copy>(frameworkTaskName) { task ->
|
||||
val embedAndSignTask = locateOrRegisterTask<FrameworkCopy>(frameworkTaskName) { task ->
|
||||
task.group = BasePlugin.BUILD_GROUP
|
||||
task.description = "Embed and sign ${framework.namePrefix} framework as requested by Xcode's environment variables"
|
||||
task.inputs.apply {
|
||||
@@ -176,17 +183,14 @@ internal fun Project.registerEmbedAndSignAppleFrameworkTask(framework: Framework
|
||||
|
||||
embedAndSignTask.configure { task ->
|
||||
task.dependsOn(assembleTask)
|
||||
task.from(appleFrameworkDir(envFrameworkSearchDir)) { it.include(framework.outputFile.name + "/**") }
|
||||
task.into(envEmbeddedFrameworksDir)
|
||||
|
||||
if (envSign != null) {
|
||||
task.doLast {
|
||||
val binary = envEmbeddedFrameworksDir
|
||||
.resolve(framework.outputFile.name)
|
||||
.resolve(framework.outputFile.nameWithoutExtension)
|
||||
exec {
|
||||
it.commandLine("codesign", "--force", "--sign", envSign, "--", binary)
|
||||
}
|
||||
task.files = files(File(appleFrameworkDir(envFrameworkSearchDir), framework.outputFile.name))
|
||||
task.destDir = envEmbeddedFrameworksDir
|
||||
if (envSign != null) task.doLast {
|
||||
val binary = envEmbeddedFrameworksDir
|
||||
.resolve(framework.outputFile.name)
|
||||
.resolve(framework.outputFile.nameWithoutExtension)
|
||||
exec {
|
||||
it.commandLine("codesign", "--force", "--sign", envSign, "--", binary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,4 +204,33 @@ private val Framework.namePrefix: String
|
||||
)
|
||||
|
||||
private fun Project.appleFrameworkDir(frameworkSearchDir: File) =
|
||||
buildDir.resolve("xcode-frameworks").resolve(frameworkSearchDir)
|
||||
buildDir.resolve("xcode-frameworks").resolve(frameworkSearchDir)
|
||||
|
||||
/**
|
||||
* macOS frameworks contain symlinks which are resolved/removed by the Gradle [Copy] task.
|
||||
* To preserve these symlinks we are using the `cp` command instead.
|
||||
* See https://youtrack.jetbrains.com/issue/KT-48594.
|
||||
*/
|
||||
private abstract class FrameworkCopy : DefaultTask() {
|
||||
|
||||
@get:InputFiles
|
||||
@get:SkipWhenEmpty
|
||||
@get:IgnoreEmptyDirectories
|
||||
@get:PathSensitive(PathSensitivity.ABSOLUTE)
|
||||
abstract var files: FileCollection
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract var destDir: File
|
||||
|
||||
@TaskAction
|
||||
fun copy() {
|
||||
destDir.mkdirs()
|
||||
files.forEach { file ->
|
||||
File(destDir, file.name).let destFile@{ destFile ->
|
||||
if (!destFile.exists()) return@destFile
|
||||
project.exec { it.commandLine("rm", "-r", destFile.absolutePath) }
|
||||
}
|
||||
project.exec { it.commandLine("cp", "-R", file.absolutePath, destDir.absolutePath) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user