From 332753b3fcf2b2fef3eddfc27536c7d5a5d1964a Mon Sep 17 00:00:00 2001 From: Timofey Solonin Date: Tue, 24 Sep 2024 16:59:26 +0200 Subject: [PATCH] Copy dSYM to DWARF_DSYM_FOLDER_PATH when archiving the app ^KT-71423 --- .../gradle/apple/XcodeDirectIntegrationIT.kt | 13 +- .../kotlin/gradle/native/AppleFrameworkIT.kt | 11 +- .../gradle/testbase/xcodeTestHelpers.kt | 6 + .../gradle/plugin/PropertiesProvider.kt | 4 + .../plugin/mpp/apple/AppleXcodeTasks.kt | 112 +++++++++++++----- .../mpp/apple/CopyDsymDuringArchiving.kt | 43 +++++++ .../plugin/mpp/apple/XcodeEnvironment.kt | 18 +++ .../gradle/unitTests/EmbedAndSignTaskTests.kt | 109 ++++++++++++++++- .../utils/ApplyEmbedAndSignEnvironment.kt | 10 ++ 9 files changed, 285 insertions(+), 41 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/CopyDsymDuringArchiving.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/apple/XcodeDirectIntegrationIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/apple/XcodeDirectIntegrationIT.kt index 6d23464f2d922..7a4508cb20eda 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/apple/XcodeDirectIntegrationIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/apple/XcodeDirectIntegrationIT.kt @@ -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 @@ -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 { 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()) @@ -107,10 +113,7 @@ class XcodeDirectIntegrationIT : KGPBaseTest() { override fun provideArguments(context: ExtensionContext): Stream { 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) } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkIT.kt index 95e76ccc7349c..652a18cd6b7f3 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkIT.kt @@ -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", @@ -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", @@ -79,6 +79,7 @@ class AppleFrameworkIT : KGPBaseTest() { build( "assembleWithoutSymbolicLinkDebugAppleFrameworkForXcodeIosArm64", + "symbolicLinkToAssembleWithoutSymbolicLinkDebugAppleFrameworkForXcodeIosArm64", "-Pkotlin.apple.createSymbolicLinkToFrameworkInBuiltProductsDir=false", environmentVariables = environmentVariables ) { @@ -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") @@ -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") @@ -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 { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/xcodeTestHelpers.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/xcodeTestHelpers.kt index d21422e3c4499..2487bf2f2fdb6 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/xcodeTestHelpers.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/xcodeTestHelpers.kt @@ -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 @@ -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") diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt index 853084b10200d..bf35b24734bb3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt @@ -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]. @@ -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") diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt index b898e71b6b0c8..1cd85f20cdc4d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt @@ -43,7 +43,13 @@ internal object AppleXcodeTasks { const val checkSandboxAndWriteProtection = "checkSandboxAndWriteProtection" } -private fun Project.registerAssembleAppleFrameworkTask(framework: Framework, environment: XcodeEnvironment): TaskProvider? { +private class AssembleFramework( + val taskProvider: TaskProvider, + val frameworkPath: Provider?, + val dsymPath: Provider?, +) + +private fun Project.registerAssembleAppleFrameworkTask(framework: Framework, environment: XcodeEnvironment): AssembleFramework? { if (!framework.konanTarget.family.isAppleFamily || !framework.konanTarget.enabledOnCurrentHostForBinariesCompilation()) return null val envTargets = environment.targets @@ -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(frameworkTaskName) { task -> - task.description = "Packs $frameworkBuildType ${frameworkTarget.name} framework for Xcode" - task.isEnabled = false - } + return AssembleFramework( + locateOrRegisterTask(frameworkTaskName) { task -> + task.description = "Packs $frameworkBuildType ${frameworkTarget.name} framework for Xcode" + task.isEnabled = false + }, + null, + null, + ) } val frameworkPath: Provider @@ -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 } @@ -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( @@ -130,6 +131,25 @@ private fun Project.registerSymbolicLinkTask( } } +private fun Project.registerDsymArchiveTask( + frameworkCopyTaskName: String, + dsymPath: Provider?, + dwarfDsymFolderPath: String?, + action: XcodeEnvironment.Action?, + isStatic: Provider, +): TaskProvider<*> { + return locateOrRegisterTask( + 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") @@ -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) } } @@ -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( @@ -233,16 +291,15 @@ private fun Project.registerEmbedTask( binary: NativeBinary, frameworkTaskName: String, environment: XcodeEnvironment, - dependencyTask: TaskProvider, embedAndSignEnabled: () -> Boolean = { true }, -) { +): TaskProvider? { 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(frameworkTaskName) { task -> task.group = BasePlugin.BUILD_GROUP @@ -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) { @@ -277,6 +333,8 @@ private fun Project.registerEmbedTask( } } } + + return embedAndSignTask } private fun Project.checkSandboxAndWriteProtectionTask( diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/CopyDsymDuringArchiving.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/CopyDsymDuringArchiving.kt new file mode 100644 index 0000000000000..e3612f7d50222 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/CopyDsymDuringArchiving.kt @@ -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 + + @get:Optional + @get:Input + abstract val dwarfDsymFolderPath: Property + + @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) + } + } + +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XcodeEnvironment.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XcodeEnvironment.kt index e8c1c038d1d65..84ec87a6f254c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XcodeEnvironment.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XcodeEnvironment.kt @@ -61,6 +61,24 @@ internal class XcodeEnvironment(private val project: Project) { val userScriptSandboxingEnabled: Boolean get() = readEnvVariable("ENABLE_USER_SCRIPT_SANDBOXING") == "YES" + enum class Action { + // xcodebuild archive + install, + // xcodebuild build/test/etc + other, + } + + val action: Action? + get() = readEnvVariable("ACTION")?.let { + when (it) { + "install" -> Action.install + else -> Action.other + } + } + + val dwarfDsymFolderPath: String? + get() = readEnvVariable("DWARF_DSYM_FOLDER_PATH") + private fun readEnvVariable(name: String): String? { return project.extensions.extraProperties.getOrNull("$XCODE_ENVIRONMENT_OVERRIDE_KEY.$name") as? String ?: System.getenv(name) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/EmbedAndSignTaskTests.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/EmbedAndSignTaskTests.kt index fe8617838e4c9..6aa97f432c2e3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/EmbedAndSignTaskTests.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/EmbedAndSignTaskTests.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.unitTests import org.gradle.api.internal.project.ProjectInternal +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.CopyDsymDuringArchiving import org.jetbrains.kotlin.gradle.plugin.mpp.apple.FrameworkCopy import org.jetbrains.kotlin.gradle.plugin.mpp.apple.SymbolicLinkToFrameworkTask import org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask @@ -108,8 +109,6 @@ class EmbedAndSignTaskTests { project.tasks.getByName("symbolicLinkToAssembleDebugAppleFrameworkForXcodeIosSimulatorArm64") ) - assembleFrameworkTask.assertDependsOn(symbolicLinkTask) - assertEquals( project.layout.buildDirectory.file("xcode-frameworks/Debug/iphonesimulator/Foo.framework").getFile(), assembleFrameworkTask.destinationFramework, @@ -151,8 +150,6 @@ class EmbedAndSignTaskTests { project.tasks.getByName("symbolicLinkToAssembleDebugAppleFrameworkForXcode") ) - assembleFrameworkTask.assertDependsOn(symbolicLinkTask) - assertEquals( project.layout.buildDirectory.file("xcode-frameworks/Debug/iphonesimulator/Foo.framework").getFile(), assembleFrameworkTask.fatFramework, @@ -204,10 +201,112 @@ class EmbedAndSignTaskTests { ) } + @Test + fun `symbolic link task - dSYM shouldn't exist when archiving`() { + assertFalse( + assertIsInstance( + embedAndSignProjectWithUniversalTargetCombination( + archs = "arm64", + isFrameworkStatic = false, + action = "install" + ).evaluate().tasks.getByName( + "symbolicLinkToAssembleDebugAppleFrameworkForXcodeIosSimulatorArm64" + ) + ).shouldDsymLinkExist.get() + ) + } + + @Test + fun `dward dsym copy task - is created disabled by default`() { + val project = embedAndSignProjectWithUniversalTargetCombination( + archs = "arm64", + dwarfDsymFolderPath = null, + isFrameworkStatic = false, + ).evaluate() + val task = assertIsInstance( + project.tasks.getByName("copyDsymForEmbedAndSignAppleFrameworkForXcode") + ) + assertEquals( + false, + task.onlyIf.isSatisfiedBy(task), + ) + assertEquals( + project.layout.buildDirectory.file("xcode-frameworks/Debug/iphonesimulator/Foo.framework.dSYM").getFile(), + task.dsymPath.orNull, + ) + assertEquals( + null, + task.dwarfDsymFolderPath.orNull, + ) + } + + @Test + fun `dward dsym copy task - is enabled by archive action`() { + val project = embedAndSignProjectWithUniversalTargetCombination( + archs = "arm64", + dwarfDsymFolderPath = "/path/to/dsym_folder", + isFrameworkStatic = false, + action = "install", + ).evaluate() + val task = assertIsInstance( + project.tasks.getByName("copyDsymForEmbedAndSignAppleFrameworkForXcode") + ) + assertEquals( + true, + task.onlyIf.isSatisfiedBy(task), + ) + assertEquals( + project.layout.buildDirectory.file("xcode-frameworks/Debug/iphonesimulator/Foo.framework.dSYM").getFile(), + task.dsymPath.orNull, + ) + assertEquals( + "/path/to/dsym_folder", + task.dwarfDsymFolderPath.orNull, + ) + } + + @Test + fun `dward dsym copy task - is disabled for static framework`() { + val project = embedAndSignProjectWithUniversalTargetCombination( + archs = "arm64", + dwarfDsymFolderPath = "/path/to/dsym_folder", + isFrameworkStatic = true, + action = "install", + ).evaluate() + val task = assertIsInstance( + project.tasks.getByName("copyDsymForEmbedAndSignAppleFrameworkForXcode") + ) + assertEquals( + false, + task.onlyIf.isSatisfiedBy(task), + ) + } + + + @Test + fun `dward dsym copy task - has an explicit dependecy on symbolic link task - because symbolic link task does clean-up for KT-68257`() { + val project = embedAndSignProjectWithUniversalTargetCombination( + archs = "arm64", + dwarfDsymFolderPath = "/path/to/dsym_folder", + isFrameworkStatic = false, + action = "install", + ).evaluate() + val task = assertIsInstance( + project.tasks.getByName("copyDsymForEmbedAndSignAppleFrameworkForXcode") + ) + assert( + task.taskDependencies.getDependencies(null).contains( + project.tasks.getByName("symbolicLinkToAssembleDebugAppleFrameworkForXcodeIosSimulatorArm64") + ) + ) + } + private fun embedAndSignProjectWithUniversalTargetCombination( archs: String, builtProductsDirectory: String = "/dd", + dwarfDsymFolderPath: String? = null, isFrameworkStatic: Boolean? = null, + action: String = "build", ): ProjectInternal { return buildProjectWithMPP( preApplyCode = { @@ -216,6 +315,8 @@ class EmbedAndSignTaskTests { sdk = "iphonesimulator", archs = archs, builtProductsDirectory = builtProductsDirectory, + dwarfDsymFolderPath = dwarfDsymFolderPath, + action = action, ) } ) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/utils/ApplyEmbedAndSignEnvironment.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/utils/ApplyEmbedAndSignEnvironment.kt index 23fa3ad377fa7..5e5500c92f8c2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/utils/ApplyEmbedAndSignEnvironment.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/utils/ApplyEmbedAndSignEnvironment.kt @@ -15,6 +15,8 @@ fun Project.applyEmbedAndSignEnvironment( archs: String, builtProductsDirectory: String = layout.buildDirectory.dir("products").getFile().canonicalPath, targetBuildDirectory: String = layout.buildDirectory.dir("buildDir").getFile().canonicalPath, + dwarfDsymFolderPath: String? = null, + action: String = "build", ) { extensions.extraProperties.set( "${XcodeEnvironment.XCODE_ENVIRONMENT_OVERRIDE_KEY}.CONFIGURATION", @@ -48,4 +50,12 @@ fun Project.applyEmbedAndSignEnvironment( "${XcodeEnvironment.XCODE_ENVIRONMENT_OVERRIDE_KEY}.ENABLE_USER_SCRIPT_SANDBOXING", "NO" ) + extensions.extraProperties.set( + "${XcodeEnvironment.XCODE_ENVIRONMENT_OVERRIDE_KEY}.DWARF_DSYM_FOLDER_PATH", + dwarfDsymFolderPath, + ) + extensions.extraProperties.set( + "${XcodeEnvironment.XCODE_ENVIRONMENT_OVERRIDE_KEY}.ACTION", + action + ) } \ No newline at end of file