Skip to content

Commit

Permalink
Copy dSYM to DWARF_DSYM_FOLDER_PATH when archiving the app
Browse files Browse the repository at this point in the history
^KT-71423
  • Loading branch information
timofey-solonin authored and qodana-bot committed Sep 25, 2024
1 parent 6b28b52 commit 332753b
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ class XcodeDirectIntegrationIT : KGPBaseTest() {
buildXcodeProject(
xcodeproj = projectPath.resolve("iosApp$BuildPhase/iosApp.xcodeproj"),
action = XcodeBuildAction.Archive(archivePath.toFile().path),
destination = "generic/platform=iOS",
buildSettingOverrides = mapOf(
// Disable signing. We are building the device but don't have an identity in the Keychain
"CODE_SIGN_IDENTITY" to "",
)
)

// Sanity check
Expand All @@ -87,13 +92,14 @@ class XcodeDirectIntegrationIT : KGPBaseTest() {
assertDirectoryExists(frameworkPath)
assert(!frameworkPath.isSymbolicLink()) { "${frameworkPath} is a symbolic link" }
assertDirectoryExists(dsymPath)
assert(dsymPath.isSymbolicLink()) { "${dsymPath} is a symbolic link" }
assert(!dsymPath.isSymbolicLink()) { "${dsymPath} is a symbolic link" }

fun dumpUuid(binary: File): List<String> {
return runProcess(listOf("otool", "-l", binary.path), projectPath.toFile())
.output.lines().filter { it.contains("uuid") }
}

// Framework and the dSYM binary must have identical UUID
val frameworkUuids = dumpUuid(frameworkPath.resolve("shared").toFile())
val dsymUuids = dumpUuid(dsymPath.resolve("Contents/Resources/DWARF/shared").toFile())

Expand All @@ -107,10 +113,7 @@ class XcodeDirectIntegrationIT : KGPBaseTest() {
override fun provideArguments(context: ExtensionContext): Stream<out Arguments> {
return super.provideArguments(context).flatMap { arguments ->
val gradleVersion = arguments.get().first()
Stream.of(
// true,
false,
).map { isStatic ->
Stream.of(true, false).map { isStatic ->
Arguments.of(gradleVersion, isStatic)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class AppleFrameworkIT : KGPBaseTest() {
"BUILT_PRODUCTS_DIR" to projectPath.resolve("shared/build/builtProductsDir").toString(),
)

build("assembleDebugAppleFrameworkForXcodeIosArm64", environmentVariables = environmentVariables) {
build("assembleDebugAppleFrameworkForXcodeIosArm64", "symbolicLinkToAssembleDebugAppleFrameworkForXcodeIosArm64", environmentVariables = environmentVariables) {
assertTasksExecuted(":shared:assembleDebugAppleFrameworkForXcodeIosArm64")
assertSymlinkInProjectPointsToProjectPath(
"shared/build/builtProductsDir/sdk.framework",
Expand All @@ -63,7 +63,7 @@ class AppleFrameworkIT : KGPBaseTest() {

build("clean")

build("assembleCustomDebugAppleFrameworkForXcodeIosArm64", environmentVariables = environmentVariables) {
build("assembleCustomDebugAppleFrameworkForXcodeIosArm64", "symbolicLinkToAssembleCustomDebugAppleFrameworkForXcodeIosArm64", environmentVariables = environmentVariables) {
assertTasksExecuted(":shared:assembleCustomDebugAppleFrameworkForXcodeIosArm64")
assertSymlinkInProjectPointsToProjectPath(
"shared/build/builtProductsDir/lib.framework",
Expand All @@ -79,6 +79,7 @@ class AppleFrameworkIT : KGPBaseTest() {

build(
"assembleWithoutSymbolicLinkDebugAppleFrameworkForXcodeIosArm64",
"symbolicLinkToAssembleWithoutSymbolicLinkDebugAppleFrameworkForXcodeIosArm64",
"-Pkotlin.apple.createSymbolicLinkToFrameworkInBuiltProductsDir=false",
environmentVariables = environmentVariables
) {
Expand Down Expand Up @@ -108,7 +109,7 @@ class AppleFrameworkIT : KGPBaseTest() {
"ARCHS" to "arm64 x86_64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use",
"BUILT_PRODUCTS_DIR" to projectPath.resolve("shared/build/xcode-frameworks/Release/iphonesimulator").toString(),
"BUILT_PRODUCTS_DIR" to projectPath.resolve("shared/build/builtProductsDir").toString(),
)
build("assembleReleaseAppleFrameworkForXcode", environmentVariables = environmentVariables) {
assertTasksExecuted(":shared:linkReleaseFrameworkIosSimulatorArm64")
Expand Down Expand Up @@ -143,7 +144,7 @@ class AppleFrameworkIT : KGPBaseTest() {
"EXPANDED_CODE_SIGN_IDENTITY" to "-",
"TARGET_BUILD_DIR" to testBuildDir.toString(),
"FRAMEWORKS_FOLDER_PATH" to "build/xcode-derived",
"BUILT_PRODUCTS_DIR" to projectPath.resolve("shared/build/xcode-frameworks/debug/macosx").toString(),
"BUILT_PRODUCTS_DIR" to projectPath.resolve("shared/build/builtProductsDir").toString(),
)
build(":shared:embedAndSignAppleFrameworkForXcode", environmentVariables = environmentVariables) {
assertTasksExecuted(":shared:assembleDebugAppleFrameworkForXcodeMacosX64")
Expand Down Expand Up @@ -376,7 +377,7 @@ class AppleFrameworkIT : KGPBaseTest() {
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use",
"BUILT_PRODUCTS_DIR" to projectPath.resolve("shared/build/xcode-frameworks/debug/iphoneos123").toString(),
"BUILT_PRODUCTS_DIR" to projectPath.resolve("shared/build/builtProductsDir").toString(),
)

subProject("shared").buildGradleKts.modify {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.gradle.util.assertProcessRunResult
import org.jetbrains.kotlin.gradle.util.modify
import org.jetbrains.kotlin.gradle.util.runProcess
import java.nio.file.Path
import java.util.*
import kotlin.io.path.exists
import kotlin.test.assertEquals

Expand Down Expand Up @@ -126,6 +127,11 @@ internal fun TestProject.prepareForXcodebuild(appendToProperties: () -> String =
buildOptions.konanDataDir?.let { konanDataDir ->
it.append("konan.data.dir=${konanDataDir.toAbsolutePath().normalize()}")
}
val configurationCacheFlag = buildOptions.configurationCache.toBooleanFlag(gradleVersion)
if (configurationCacheFlag != null) {
it.append("org.gradle.unsafe.configuration-cache=$configurationCacheFlag")
it.append("org.gradle.unsafe.configuration-cache-problems=${buildOptions.configurationCacheProblems.name.lowercase(Locale.getDefault())}")
}
}

build(":wrapper")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,9 @@ internal class PropertiesProvider private constructor(private val project: Proje
val appleCreateSymbolicLinkToFrameworkInBuiltProductsDir: Boolean
get() = booleanProperty(PropertyNames.KOTLIN_APPLE_CREATE_SYMBOLIC_LINK_TO_FRAMEWORK_IN_BUILT_PRODUCTS_DIR) ?: true

val appleCopyDsymDuringArchiving: Boolean
get() = booleanProperty(PropertyNames.KOTLIN_APPLE_COPY_DSYM_DURING_ARCHIVING) ?: true


/**
* Allows suppressing the diagnostic [KotlinToolingDiagnostics.BuildToolsApiVersionInconsistency].
Expand Down Expand Up @@ -672,6 +675,7 @@ internal class PropertiesProvider private constructor(private val project: Proje
val KOTLIN_PROJECT_PERSISTENT_DIR = property("kotlin.project.persistent.dir")
val KOTLIN_PROJECT_PERSISTENT_DIR_GRADLE_DISABLE_WRITE = property("kotlin.project.persistent.dir.gradle.disableWrite")
val KOTLIN_APPLE_CREATE_SYMBOLIC_LINK_TO_FRAMEWORK_IN_BUILT_PRODUCTS_DIR = property("kotlin.apple.createSymbolicLinkToFrameworkInBuiltProductsDir")
val KOTLIN_APPLE_COPY_DSYM_DURING_ARCHIVING = property("kotlin.apple.copyDsymDuringArchiving")
val KOTLIN_APPLE_XCODE_COMPATIBILITY_NOWARN = property("kotlin.apple.xcodeCompatibility.nowarn")
val KOTLIN_APPLE_COCOAPODS_EXECUTABLE = property("kotlin.apple.cocoapods.bin")
val KOTLIN_APPLE_ALLOW_EMBED_AND_SIGN_WITH_COCOAPODS = property("kotlin.apple.deprecated.allowUsingEmbedAndSignWithCocoaPodsDependencies")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ internal object AppleXcodeTasks {
const val checkSandboxAndWriteProtection = "checkSandboxAndWriteProtection"
}

private fun Project.registerAssembleAppleFrameworkTask(framework: Framework, environment: XcodeEnvironment): TaskProvider<out Task>? {
private class AssembleFramework(
val taskProvider: TaskProvider<out Task>,
val frameworkPath: Provider<File>?,
val dsymPath: Provider<File>?,
)

private fun Project.registerAssembleAppleFrameworkTask(framework: Framework, environment: XcodeEnvironment): AssembleFramework? {
if (!framework.konanTarget.family.isAppleFamily || !framework.konanTarget.enabledOnCurrentHostForBinariesCompilation()) return null

val envTargets = environment.targets
Expand All @@ -66,16 +72,15 @@ private fun Project.registerAssembleAppleFrameworkTask(framework: Framework, env
return null
}

val symbolicLinkTask = registerSymbolicLinkTask(
frameworkCopyTaskName = frameworkTaskName,
builtProductsDir = builtProductsDir(frameworkTaskName, environment),
)

if (!isRequestedFramework) {
return locateOrRegisterTask<DefaultTask>(frameworkTaskName) { task ->
task.description = "Packs $frameworkBuildType ${frameworkTarget.name} framework for Xcode"
task.isEnabled = false
}
return AssembleFramework(
locateOrRegisterTask<DefaultTask>(frameworkTaskName) { task ->
task.description = "Packs $frameworkBuildType ${frameworkTarget.name} framework for Xcode"
task.isEnabled = false
},
null,
null,
)
}

val frameworkPath: Provider<File>
Expand All @@ -87,7 +92,6 @@ private fun Project.registerAssembleAppleFrameworkTask(framework: Framework, env
task.baseName = framework.baseName
task.destinationDirProperty.fileProvider(appleFrameworkDir(frameworkTaskName, environment))
task.isEnabled = frameworkBuildType == envBuildType
task.dependsOn(symbolicLinkTask)
}.also { taskProvider ->
taskProvider.configure { task -> task.from(framework) }
frameworkPath = taskProvider.map { it.fatFramework }
Expand All @@ -99,20 +103,17 @@ private fun Project.registerAssembleAppleFrameworkTask(framework: Framework, env
task.sourceFramework.fileProvider(framework.linkTaskProvider.map { it.outputFile.get() })
task.sourceDsym.fileProvider(dsymFile(task.sourceFramework.mapToFile()))
task.destinationDirectory.fileProvider(appleFrameworkDir(frameworkTaskName, environment))
task.dependsOn(symbolicLinkTask)
}.also { taskProvider ->
frameworkPath = taskProvider.map { it.destinationFramework }
dsymPath = taskProvider.map { it.destinationDsym }
}
}

symbolicLinkTask.configure {
it.frameworkPath.set(frameworkPath)
it.dsymPath.set(dsymPath)
it.shouldDsymLinkExist.set(!framework.isStatic)
}

return assembleFrameworkTask
return AssembleFramework(
assembleFrameworkTask,
frameworkPath,
dsymPath,
)
}

private fun Project.registerSymbolicLinkTask(
Expand All @@ -130,6 +131,25 @@ private fun Project.registerSymbolicLinkTask(
}
}

private fun Project.registerDsymArchiveTask(
frameworkCopyTaskName: String,
dsymPath: Provider<File>?,
dwarfDsymFolderPath: String?,
action: XcodeEnvironment.Action?,
isStatic: Provider<Boolean>,
): TaskProvider<*> {
return locateOrRegisterTask<CopyDsymDuringArchiving>(
lowerCamelCaseName(
"copyDsymFor",
frameworkCopyTaskName
)
) { task ->
task.onlyIf { action == XcodeEnvironment.Action.install && !isStatic.get() }
task.dwarfDsymFolderPath.set(dwarfDsymFolderPath)
dsymPath?.let { task.dsymPath.set(it) }
}
}

private fun fireEnvException(frameworkTaskName: String, environment: XcodeEnvironment): Nothing {
val envBuildType = environment.buildType
val envConfiguration = System.getenv("CONFIGURATION")
Expand Down Expand Up @@ -180,7 +200,9 @@ internal fun Project.registerEmbedSwiftExportTask(
swiftExportTask.dependsOn(sandBoxTask)
binary.linkTaskProvider.dependsOn(sandBoxTask)

registerEmbedTask(binary, binaryTaskName, environment, swiftExportTask) { false }
val embedAndSignTask = registerEmbedTask(binary, binaryTaskName, environment) { false } ?: return

embedAndSignTask.dependsOn(swiftExportTask)
}
}

Expand All @@ -199,10 +221,46 @@ internal fun Project.registerEmbedAndSignAppleFrameworkTask(framework: Framework
val sandBoxTask = checkSandboxAndWriteProtectionTask(environment, environment.userScriptSandboxingEnabled)
val assembleTask = registerAssembleAppleFrameworkTask(framework, environment) ?: return

assembleTask.dependsOn(sandBoxTask)
val symbolicLinkTask = registerSymbolicLinkTask(
frameworkCopyTaskName = assembleTask.taskProvider.name,
builtProductsDir = builtProductsDir(frameworkTaskName, environment),
)
symbolicLinkTask.configure { task ->
assembleTask.frameworkPath?.let { task.frameworkPath.set(it) }
assembleTask.dsymPath?.let { task.dsymPath.set(it) }
task.shouldDsymLinkExist.set(
provider {
when (environment.action) {
// Don't create symbolic link for dSYM when archiving: KT-71423
XcodeEnvironment.Action.install -> false
XcodeEnvironment.Action.other,
null -> !framework.isStatic
}
}
)
}

assembleTask.taskProvider.dependsOn(sandBoxTask)
framework.linkTaskProvider.dependsOn(sandBoxTask)

registerEmbedTask(framework, frameworkTaskName, environment, assembleTask) { !framework.isStatic }
val embedAndSignTask = registerEmbedTask(framework, frameworkTaskName, environment) { !framework.isStatic } ?: return
embedAndSignTask.dependsOn(assembleTask.taskProvider)
embedAndSignTask.dependsOn(symbolicLinkTask)

if (kotlinPropertiesProvider.appleCopyDsymDuringArchiving) {
val dsymCopyTask = registerDsymArchiveTask(
frameworkCopyTaskName = frameworkTaskName,
dsymPath = assembleTask.dsymPath,
dwarfDsymFolderPath = environment.dwarfDsymFolderPath,
action = environment.action,
isStatic = provider { framework.isStatic },
)
// FIXME: KT-71720
// Dsym copy task must execute after symbolic link task because symbolic link task does clean up for KT-68257 and dSYM is copied to the same location
dsymCopyTask.dependsOn(symbolicLinkTask)
dsymCopyTask.dependsOn(assembleTask.taskProvider)
embedAndSignTask.dependsOn(dsymCopyTask)
}
}

private fun Project.isRunWithXcodeEnvironment(
Expand Down Expand Up @@ -233,16 +291,15 @@ private fun Project.registerEmbedTask(
binary: NativeBinary,
frameworkTaskName: String,
environment: XcodeEnvironment,
dependencyTask: TaskProvider<out Task>,
embedAndSignEnabled: () -> Boolean = { true },
) {
): TaskProvider<out Task>? {
val envBuildType = environment.buildType
val envTargets = environment.targets
val envEmbeddedFrameworksDir = environment.embeddedFrameworksDir
val envSign = environment.sign
val userScriptSandboxingEnabled = environment.userScriptSandboxingEnabled

if (envBuildType == null || envTargets.isEmpty() || envEmbeddedFrameworksDir == null) return
if (envBuildType == null || envTargets.isEmpty() || envEmbeddedFrameworksDir == null) return null

val embedAndSignTask = locateOrRegisterTask<EmbedAndSignTask>(frameworkTaskName) { task ->
task.group = BasePlugin.BUILD_GROUP
Expand All @@ -259,11 +316,10 @@ private fun Project.registerEmbedTask(
}
}

if (binary.buildType != envBuildType || !envTargets.contains(binary.konanTarget)) return
if (binary.buildType != envBuildType || !envTargets.contains(binary.konanTarget)) return null

embedAndSignTask.configure { task ->
val frameworkFile = binary.outputFile
task.dependsOn(dependencyTask)
task.sourceFramework.fileProvider(appleFrameworkDir(frameworkTaskName, environment).map { it.resolve(frameworkFile.name) })
task.destinationDirectory.set(envEmbeddedFrameworksDir)
if (envSign != null) {
Expand All @@ -277,6 +333,8 @@ private fun Project.registerEmbedTask(
}
}
}

return embedAndSignTask
}

private fun Project.checkSandboxAndWriteProtectionTask(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/

package org.jetbrains.kotlin.gradle.plugin.mpp.apple

import org.gradle.api.DefaultTask
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction
import org.gradle.work.DisableCachingByDefault
import java.io.File
import javax.inject.Inject

@DisableCachingByDefault(because = "This task only does copying")
internal abstract class CopyDsymDuringArchiving @Inject constructor(
private val fsOperations: FileSystemOperations,
) : DefaultTask() {

@get:Internal
abstract val dsymPath: Property<File>

@get:Optional
@get:Input
abstract val dwarfDsymFolderPath: Property<String>

@TaskAction
fun copy() {
val dwarfDsymFolderPath = this.dwarfDsymFolderPath.orNull ?: return
if (dwarfDsymFolderPath.isEmpty()) return
fsOperations.copy {
it.from(dsymPath.get()) { innerSpec ->
innerSpec.into(dsymPath.get().name)
}
it.into(dwarfDsymFolderPath)
}
}

}
Loading

0 comments on commit 332753b

Please sign in to comment.