From 65e8e69c24dcea4b0f002103a619f55727501a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20=C5=A0olar?= Date: Thu, 20 Oct 2022 09:37:21 +0200 Subject: [PATCH] uikit deploy, split Gradle and Xcode (#2406) --- .../configureExperimentalUikitApplication.kt | 36 ++----------- .../uikit/internal/configureIosDeployTasks.kt | 14 +++-- ...PackComposeUiKitApplicationForXCodeTask.kt | 42 +++++++++++++++ .../configureTaskToGenerateXcodeProject.kt | 3 -- .../internal/registerConnectedDeviceTasks.kt | 27 ++++++---- .../uikit/internal/registerSimulatorTasks.kt | 51 +++++++++++-------- ...entalPackComposeApplicationForXCodeTask.kt | 3 ++ .../jetbrains/compose/internal/gradleUtils.kt | 3 ++ 8 files changed, 111 insertions(+), 68 deletions(-) create mode 100644 gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configurePackComposeUiKitApplicationForXCodeTask.kt diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureExperimentalUikitApplication.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureExperimentalUikitApplication.kt index 0c09b6fcb7..0d67a1dc4c 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureExperimentalUikitApplication.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureExperimentalUikitApplication.kt @@ -9,12 +9,7 @@ import org.gradle.api.Project import org.jetbrains.compose.desktop.application.internal.OS import org.jetbrains.compose.desktop.application.internal.currentOS import org.jetbrains.compose.experimental.dsl.ExperimentalUiKitApplication -import org.jetbrains.compose.experimental.uikit.tasks.ExperimentalPackComposeApplicationForXCodeTask -import org.jetbrains.compose.experimental.uikit.tasks.ExperimentalPackComposeApplicationForXCodeTask.UikitTarget -import org.jetbrains.compose.internal.toDir import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget -import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType internal fun Project.configureExperimentalUikitApplication( mppExt: KotlinMultiplatformExtension, @@ -22,31 +17,8 @@ internal fun Project.configureExperimentalUikitApplication( ) { if (currentOS != OS.MacOS) return - tasks.register( - "packComposeUikitApplicationForXCode", - ExperimentalPackComposeApplicationForXCodeTask::class.java - ) { packTask -> - val targetType = project.providers.environmentVariable("SDK_NAME").map { - if (it.startsWith("iphoneos")) - UikitTarget.Arm64 - else UikitTarget.X64 - }.orElse(UikitTarget.X64) - val buildType = project.providers.environmentVariable("CONFIGURATION").map { - if (it.equals("release", ignoreCase = true)) NativeBuildType.RELEASE - else NativeBuildType.DEBUG - }.orElse(NativeBuildType.DEBUG) - val target = mppExt.targets.getByName(targetType.get().targetName) as KotlinNativeTarget - val kotlinBinary = target.binaries.getExecutable(buildType.get()) - val targetBuildDir = project.providers.environmentVariable("TARGET_BUILD_DIR").toDir(project) - val executablePath = project.providers.environmentVariable("EXECUTABLE_PATH") - - packTask.targetType.set(targetType) - packTask.buildType.set(buildType) - packTask.dependsOn(kotlinBinary.linkTask) - packTask.kotlinBinary.set(kotlinBinary.outputFile) - packTask.destinationDir.set(targetBuildDir) - packTask.executablePath.set(executablePath) - } - - configureIosDeployTasks(application) + configureIosDeployTasks( + mppExt = mppExt, + application = application, + ) } diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureIosDeployTasks.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureIosDeployTasks.kt index e08aaad518..2a2e3f006b 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureIosDeployTasks.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureIosDeployTasks.kt @@ -5,20 +5,26 @@ package org.jetbrains.compose.experimental.uikit.internal -import org.gradle.api.* +import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.jetbrains.compose.experimental.dsl.DeployTarget import org.jetbrains.compose.experimental.dsl.ExperimentalUiKitApplication +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension const val SDK_PREFIFX_SIMULATOR = "iphonesimulator" const val SDK_PREFIX_IPHONEOS = "iphoneos" const val TEAM_ID_PROPERTY_KEY = "compose.ios.teamId" -const val RELATIVE_PRODUCTS_PATH = "build/Build/Products" +const val BUILD_DIR_NAME = "build" +const val RELATIVE_PRODUCTS_PATH = "$BUILD_DIR_NAME/Build/Products" fun Project.getBuildIosDir(id: String) = buildDir.resolve("ios").resolve(id) -internal fun Project.configureIosDeployTasks(application: ExperimentalUiKitApplication) { +internal fun Project.configureIosDeployTasks( + mppExt: KotlinMultiplatformExtension, + application: ExperimentalUiKitApplication, +) { val projectName = application.projectName val bundleIdPrefix = application.bundleIdPrefix @@ -30,6 +36,7 @@ internal fun Project.configureIosDeployTasks(application: ExperimentalUiKitAppli when (target.deploy) { is DeployTarget.Simulator -> { registerSimulatorTasks( + mppExt = mppExt, id = id, deploy = target.deploy, projectName = projectName, @@ -43,6 +50,7 @@ internal fun Project.configureIosDeployTasks(application: ExperimentalUiKitAppli } is DeployTarget.ConnectedDevice -> { registerConnectedDeviceTasks( + mppExt = mppExt, id = id, deploy = target.deploy, projectName = projectName, diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configurePackComposeUiKitApplicationForXCodeTask.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configurePackComposeUiKitApplicationForXCodeTask.kt new file mode 100644 index 0000000000..abd106cf84 --- /dev/null +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configurePackComposeUiKitApplicationForXCodeTask.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package org.jetbrains.compose.experimental.uikit.internal + +import org.gradle.api.Project +import org.jetbrains.compose.experimental.uikit.tasks.ExperimentalPackComposeApplicationForXCodeTask +import org.jetbrains.compose.internal.fileToDir +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType +import java.io.File + +internal fun Project.configurePackComposeUiKitApplicationForXCodeTask( + mppExt: KotlinMultiplatformExtension, + id: String, + configName: String, + projectName: String, + targetBuildPath: File, + targetType: ExperimentalPackComposeApplicationForXCodeTask.UikitTarget, +) = tasks.register( + "packComposeUikitApplicationForXCode$id$configName", + ExperimentalPackComposeApplicationForXCodeTask::class.java +) { packTask -> + val buildType = project.provider { configName }.map { + if (it.equals("release", ignoreCase = true)) NativeBuildType.RELEASE + else NativeBuildType.DEBUG + }.orElse(NativeBuildType.DEBUG) + val target = mppExt.targets.getByName(targetType.targetName) as KotlinNativeTarget + val kotlinBinary = target.binaries.getExecutable(buildType.get()) + val targetBuildDir = project.provider { targetBuildPath }.fileToDir(project) + val executablePath = "${projectName}.app/${projectName}" + + packTask.targetType.set(targetType) + packTask.buildType.set(buildType) + packTask.dependsOn(kotlinBinary.linkTask) + packTask.kotlinBinary.set(kotlinBinary.outputFile) + packTask.destinationDir.set(targetBuildDir) + packTask.executablePath.set(executablePath) +} diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureTaskToGenerateXcodeProject.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureTaskToGenerateXcodeProject.kt index 20fb089a57..4cb618cec9 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureTaskToGenerateXcodeProject.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureTaskToGenerateXcodeProject.kt @@ -37,9 +37,6 @@ internal fun Project.configureTaskToGenerateXcodeProject( type: application platform: iOS deploymentTarget: "12.0" - prebuildScripts: - - script: cd "${rootDir.absolutePath}" && ./gradlew -i -p . packComposeUikitApplicationForXCode - name: GradleCompile info: path: plists/Ios/Info.plist settings: diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerConnectedDeviceTasks.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerConnectedDeviceTasks.kt index 6c41e4c0b8..08a4d9979e 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerConnectedDeviceTasks.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerConnectedDeviceTasks.kt @@ -5,16 +5,19 @@ package org.jetbrains.compose.experimental.uikit.internal -import org.gradle.api.* +import org.gradle.api.Project import org.gradle.api.tasks.TaskProvider import org.jetbrains.compose.desktop.application.internal.MacUtils import org.jetbrains.compose.experimental.dsl.DeployTarget import org.jetbrains.compose.experimental.dsl.UiKitConfiguration import org.jetbrains.compose.experimental.uikit.tasks.AbstractComposeIosTask +import org.jetbrains.compose.experimental.uikit.tasks.ExperimentalPackComposeApplicationForXCodeTask import org.jetbrains.compose.internal.getLocalProperty import org.jetbrains.compose.internal.localPropertiesFile +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension fun Project.registerConnectedDeviceTasks( + mppExt: KotlinMultiplatformExtension, id: String, deploy: DeployTarget.ConnectedDevice, projectName: String, @@ -44,21 +47,27 @@ fun Project.registerConnectedDeviceTasks( for (configuration in configurations) { val configName = configuration.name - val iosCompiledAppDir = xcodeProjectDir.resolve(RELATIVE_PRODUCTS_PATH) - .resolve("$configName-iphoneos/${projectName}.app") + val targetBuildPath = xcodeProjectDir.resolve(RELATIVE_PRODUCTS_PATH) + .resolve("$configName-iphoneos") + val iosCompiledAppDir = targetBuildPath.resolve("${projectName}.app") + + val taskPackageUiKitAppForXcode = configurePackComposeUiKitApplicationForXCodeTask( + mppExt = mppExt, + id = id, + configName = configName, + projectName = projectName, + targetBuildPath = targetBuildPath, + targetType = ExperimentalPackComposeApplicationForXCodeTask.UikitTarget.Arm64, + ) val taskBuild = tasks.composeIosTask("iosBuildIphoneOs$id$configName") { dependsOn(taskGenerateXcodeProject) + dependsOn(taskPackageUiKitAppForXcode) doLast { // xcrun xcodebuild -showsdks (list all sdk) val sdk = SDK_PREFIX_IPHONEOS + getSimctlListData().runtimes.first().version val scheme = projectName // xcrun xcodebuild -list -project . (list all schemes) - val buildDir = "build" - - // cleanup build directory as xcodebuild does not do it (provoking unexpected side effects). - project.delete(xcodeProjectDir.resolve(buildDir)) - runExternalTool( MacUtils.xcrun, listOf( @@ -66,7 +75,7 @@ fun Project.registerConnectedDeviceTasks( "-scheme", scheme, "-project", ".", "-configuration", configName, - "-derivedDataPath", buildDir, + "-derivedDataPath", BUILD_DIR_NAME, "-arch", "arm64", "-sdk", sdk, "-allowProvisioningUpdates", diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerSimulatorTasks.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerSimulatorTasks.kt index eee6cb4169..bbc9e867ed 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerSimulatorTasks.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerSimulatorTasks.kt @@ -5,7 +5,7 @@ package org.jetbrains.compose.experimental.uikit.internal -import org.gradle.api.* +import org.gradle.api.Project import org.gradle.api.tasks.TaskProvider import org.jetbrains.compose.desktop.application.internal.Arch import org.jetbrains.compose.desktop.application.internal.MacUtils @@ -13,8 +13,11 @@ import org.jetbrains.compose.desktop.application.internal.currentArch import org.jetbrains.compose.experimental.dsl.DeployTarget import org.jetbrains.compose.experimental.dsl.UiKitConfiguration import org.jetbrains.compose.experimental.uikit.tasks.AbstractComposeIosTask +import org.jetbrains.compose.experimental.uikit.tasks.ExperimentalPackComposeApplicationForXCodeTask +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension fun Project.registerSimulatorTasks( + mppExt: KotlinMultiplatformExtension, id: String, deploy: DeployTarget.Simulator, projectName: String, @@ -92,33 +95,39 @@ fun Project.registerSimulatorTasks( for (configuration in configurations) { val configName = configuration.name - val iosCompiledAppDir = xcodeProjectDir.resolve(RELATIVE_PRODUCTS_PATH) - .resolve("$configName-iphonesimulator/${projectName}.app") + val targetBuildPath = xcodeProjectDir.resolve(RELATIVE_PRODUCTS_PATH) + .resolve("$configName-iphonesimulator") + val iosCompiledAppDir = targetBuildPath.resolve("${projectName}.app") + + val taskPackageUiKitAppForXcode = configurePackComposeUiKitApplicationForXCodeTask( + mppExt = mppExt, + id = id, + configName = configName, + projectName = projectName, + targetBuildPath = targetBuildPath, + targetType = ExperimentalPackComposeApplicationForXCodeTask.UikitTarget.X64, + ) val taskBuild = tasks.composeIosTask("iosSimulatorBuild$id$configName") { dependsOn(taskGenerateXcodeProject) + dependsOn(taskPackageUiKitAppForXcode) doLast { // xcrun xcodebuild -showsdks (list all sdk) val sdk = SDK_PREFIFX_SIMULATOR + getSimctlListData().runtimes.first().version val scheme = projectName // xcrun xcodebuild -list -project . (list all schemes) - repeat(2) { - // todo repeat(2) is workaround of error (domain=NSPOSIXErrorDomain, code=22) - // The bundle identifier of the application could not be determined - // Ensure that the application's Info.plist contains a value for CFBundleIdentifier. - runExternalTool( - MacUtils.xcrun, - listOf( - "xcodebuild", - "-scheme", scheme, - "-project", ".", - "-configuration", configName, - "-derivedDataPath", "build", - "-arch", simulatorArch, - "-sdk", sdk - ), - workingDir = xcodeProjectDir - ) - } + runExternalTool( + MacUtils.xcrun, + listOf( + "xcodebuild", + "-scheme", scheme, + "-project", ".", + "-configuration", configName, + "-derivedDataPath", BUILD_DIR_NAME, + "-arch", simulatorArch, + "-sdk", sdk + ), + workingDir = xcodeProjectDir + ) } } diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/tasks/ExperimentalPackComposeApplicationForXCodeTask.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/tasks/ExperimentalPackComposeApplicationForXCodeTask.kt index 5b308cb5a4..efad8d3307 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/tasks/ExperimentalPackComposeApplicationForXCodeTask.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/tasks/ExperimentalPackComposeApplicationForXCodeTask.kt @@ -34,6 +34,8 @@ abstract class ExperimentalPackComposeApplicationForXCodeTask : DefaultTask() { @TaskAction fun run() { val destinationDir = destinationDir.get().asFile + project.delete(destinationDir) + project.mkdir(destinationDir) val executableSource = kotlinBinary.get().asFile val dsymSource = File(executableSource.absolutePath + ".dSYM") @@ -52,6 +54,7 @@ abstract class ExperimentalPackComposeApplicationForXCodeTask : DefaultTask() { } } + executableDestination.parentFile.mkdirs() // We need to preserve executable flag for resulting executable, "FileKt.copyTo" extension method does not allow this. Files.copy(executableSource.toPath(), executableDestination.toPath(), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING) } diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/gradleUtils.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/gradleUtils.kt index d2a8d486fc..7b17850738 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/gradleUtils.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/gradleUtils.kt @@ -37,6 +37,9 @@ internal inline fun Project.registerTask( internal fun Provider.toDir(project: Project): Provider = project.layout.dir(map { File(it) }) +internal fun Provider.fileToDir(project: Project): Provider = + project.layout.dir(this) + val Project.localPropertiesFile get() = project.rootProject.file("local.properties") fun Project.getLocalProperty(key: String): String? {