Browse Source

uikit deploy, split Gradle and Xcode (#2406)

pull/2425/head
Blaž Šolar 2 years ago committed by GitHub
parent
commit
65e8e69c24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 36
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureExperimentalUikitApplication.kt
  2. 14
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureIosDeployTasks.kt
  3. 42
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configurePackComposeUiKitApplicationForXCodeTask.kt
  4. 3
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureTaskToGenerateXcodeProject.kt
  5. 27
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerConnectedDeviceTasks.kt
  6. 27
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerSimulatorTasks.kt
  7. 3
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/tasks/ExperimentalPackComposeApplicationForXCodeTask.kt
  8. 3
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/gradleUtils.kt

36
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.OS
import org.jetbrains.compose.desktop.application.internal.currentOS import org.jetbrains.compose.desktop.application.internal.currentOS
import org.jetbrains.compose.experimental.dsl.ExperimentalUiKitApplication 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.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
internal fun Project.configureExperimentalUikitApplication( internal fun Project.configureExperimentalUikitApplication(
mppExt: KotlinMultiplatformExtension, mppExt: KotlinMultiplatformExtension,
@ -22,31 +17,8 @@ internal fun Project.configureExperimentalUikitApplication(
) { ) {
if (currentOS != OS.MacOS) return if (currentOS != OS.MacOS) return
tasks.register( configureIosDeployTasks(
"packComposeUikitApplicationForXCode", mppExt = mppExt,
ExperimentalPackComposeApplicationForXCodeTask::class.java application = application,
) { 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)
} }

14
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 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.TaskContainer
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.jetbrains.compose.experimental.dsl.DeployTarget import org.jetbrains.compose.experimental.dsl.DeployTarget
import org.jetbrains.compose.experimental.dsl.ExperimentalUiKitApplication import org.jetbrains.compose.experimental.dsl.ExperimentalUiKitApplication
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
const val SDK_PREFIFX_SIMULATOR = "iphonesimulator" const val SDK_PREFIFX_SIMULATOR = "iphonesimulator"
const val SDK_PREFIX_IPHONEOS = "iphoneos" const val SDK_PREFIX_IPHONEOS = "iphoneos"
const val TEAM_ID_PROPERTY_KEY = "compose.ios.teamId" 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) 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 projectName = application.projectName
val bundleIdPrefix = application.bundleIdPrefix val bundleIdPrefix = application.bundleIdPrefix
@ -30,6 +36,7 @@ internal fun Project.configureIosDeployTasks(application: ExperimentalUiKitAppli
when (target.deploy) { when (target.deploy) {
is DeployTarget.Simulator -> { is DeployTarget.Simulator -> {
registerSimulatorTasks( registerSimulatorTasks(
mppExt = mppExt,
id = id, id = id,
deploy = target.deploy, deploy = target.deploy,
projectName = projectName, projectName = projectName,
@ -43,6 +50,7 @@ internal fun Project.configureIosDeployTasks(application: ExperimentalUiKitAppli
} }
is DeployTarget.ConnectedDevice -> { is DeployTarget.ConnectedDevice -> {
registerConnectedDeviceTasks( registerConnectedDeviceTasks(
mppExt = mppExt,
id = id, id = id,
deploy = target.deploy, deploy = target.deploy,
projectName = projectName, projectName = projectName,

42
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)
}

3
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureTaskToGenerateXcodeProject.kt

@ -37,9 +37,6 @@ internal fun Project.configureTaskToGenerateXcodeProject(
type: application type: application
platform: iOS platform: iOS
deploymentTarget: "12.0" deploymentTarget: "12.0"
prebuildScripts:
- script: cd "${rootDir.absolutePath}" && ./gradlew -i -p . packComposeUikitApplicationForXCode
name: GradleCompile
info: info:
path: plists/Ios/Info.plist path: plists/Ios/Info.plist
settings: settings:

27
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 package org.jetbrains.compose.experimental.uikit.internal
import org.gradle.api.* import org.gradle.api.Project
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.jetbrains.compose.desktop.application.internal.MacUtils import org.jetbrains.compose.desktop.application.internal.MacUtils
import org.jetbrains.compose.experimental.dsl.DeployTarget import org.jetbrains.compose.experimental.dsl.DeployTarget
import org.jetbrains.compose.experimental.dsl.UiKitConfiguration import org.jetbrains.compose.experimental.dsl.UiKitConfiguration
import org.jetbrains.compose.experimental.uikit.tasks.AbstractComposeIosTask 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.getLocalProperty
import org.jetbrains.compose.internal.localPropertiesFile import org.jetbrains.compose.internal.localPropertiesFile
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
fun Project.registerConnectedDeviceTasks( fun Project.registerConnectedDeviceTasks(
mppExt: KotlinMultiplatformExtension,
id: String, id: String,
deploy: DeployTarget.ConnectedDevice, deploy: DeployTarget.ConnectedDevice,
projectName: String, projectName: String,
@ -44,21 +47,27 @@ fun Project.registerConnectedDeviceTasks(
for (configuration in configurations) { for (configuration in configurations) {
val configName = configuration.name val configName = configuration.name
val iosCompiledAppDir = xcodeProjectDir.resolve(RELATIVE_PRODUCTS_PATH) val targetBuildPath = xcodeProjectDir.resolve(RELATIVE_PRODUCTS_PATH)
.resolve("$configName-iphoneos/${projectName}.app") .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<AbstractComposeIosTask>("iosBuildIphoneOs$id$configName") { val taskBuild = tasks.composeIosTask<AbstractComposeIosTask>("iosBuildIphoneOs$id$configName") {
dependsOn(taskGenerateXcodeProject) dependsOn(taskGenerateXcodeProject)
dependsOn(taskPackageUiKitAppForXcode)
doLast { doLast {
// xcrun xcodebuild -showsdks (list all sdk) // xcrun xcodebuild -showsdks (list all sdk)
val sdk = SDK_PREFIX_IPHONEOS + getSimctlListData().runtimes.first().version val sdk = SDK_PREFIX_IPHONEOS + getSimctlListData().runtimes.first().version
val scheme = projectName // xcrun xcodebuild -list -project . (list all schemes) 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( runExternalTool(
MacUtils.xcrun, MacUtils.xcrun,
listOf( listOf(
@ -66,7 +75,7 @@ fun Project.registerConnectedDeviceTasks(
"-scheme", scheme, "-scheme", scheme,
"-project", ".", "-project", ".",
"-configuration", configName, "-configuration", configName,
"-derivedDataPath", buildDir, "-derivedDataPath", BUILD_DIR_NAME,
"-arch", "arm64", "-arch", "arm64",
"-sdk", sdk, "-sdk", sdk,
"-allowProvisioningUpdates", "-allowProvisioningUpdates",

27
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 package org.jetbrains.compose.experimental.uikit.internal
import org.gradle.api.* import org.gradle.api.Project
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.jetbrains.compose.desktop.application.internal.Arch import org.jetbrains.compose.desktop.application.internal.Arch
import org.jetbrains.compose.desktop.application.internal.MacUtils 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.DeployTarget
import org.jetbrains.compose.experimental.dsl.UiKitConfiguration import org.jetbrains.compose.experimental.dsl.UiKitConfiguration
import org.jetbrains.compose.experimental.uikit.tasks.AbstractComposeIosTask 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( fun Project.registerSimulatorTasks(
mppExt: KotlinMultiplatformExtension,
id: String, id: String,
deploy: DeployTarget.Simulator, deploy: DeployTarget.Simulator,
projectName: String, projectName: String,
@ -92,19 +95,26 @@ fun Project.registerSimulatorTasks(
for (configuration in configurations) { for (configuration in configurations) {
val configName = configuration.name val configName = configuration.name
val iosCompiledAppDir = xcodeProjectDir.resolve(RELATIVE_PRODUCTS_PATH) val targetBuildPath = xcodeProjectDir.resolve(RELATIVE_PRODUCTS_PATH)
.resolve("$configName-iphonesimulator/${projectName}.app") .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<AbstractComposeIosTask>("iosSimulatorBuild$id$configName") { val taskBuild = tasks.composeIosTask<AbstractComposeIosTask>("iosSimulatorBuild$id$configName") {
dependsOn(taskGenerateXcodeProject) dependsOn(taskGenerateXcodeProject)
dependsOn(taskPackageUiKitAppForXcode)
doLast { doLast {
// xcrun xcodebuild -showsdks (list all sdk) // xcrun xcodebuild -showsdks (list all sdk)
val sdk = SDK_PREFIFX_SIMULATOR + getSimctlListData().runtimes.first().version val sdk = SDK_PREFIFX_SIMULATOR + getSimctlListData().runtimes.first().version
val scheme = projectName // xcrun xcodebuild -list -project . (list all schemes) 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( runExternalTool(
MacUtils.xcrun, MacUtils.xcrun,
listOf( listOf(
@ -112,7 +122,7 @@ fun Project.registerSimulatorTasks(
"-scheme", scheme, "-scheme", scheme,
"-project", ".", "-project", ".",
"-configuration", configName, "-configuration", configName,
"-derivedDataPath", "build", "-derivedDataPath", BUILD_DIR_NAME,
"-arch", simulatorArch, "-arch", simulatorArch,
"-sdk", sdk "-sdk", sdk
), ),
@ -120,7 +130,6 @@ fun Project.registerSimulatorTasks(
) )
} }
} }
}
val installIosSimulator = tasks.composeIosTask<AbstractComposeIosTask>("iosSimulatorInstall$id$configName") { val installIosSimulator = tasks.composeIosTask<AbstractComposeIosTask>("iosSimulatorInstall$id$configName") {
dependsOn(taskBuild, taskBootSimulator) dependsOn(taskBuild, taskBootSimulator)

3
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/tasks/ExperimentalPackComposeApplicationForXCodeTask.kt

@ -34,6 +34,8 @@ abstract class ExperimentalPackComposeApplicationForXCodeTask : DefaultTask() {
@TaskAction @TaskAction
fun run() { fun run() {
val destinationDir = destinationDir.get().asFile val destinationDir = destinationDir.get().asFile
project.delete(destinationDir)
project.mkdir(destinationDir)
val executableSource = kotlinBinary.get().asFile val executableSource = kotlinBinary.get().asFile
val dsymSource = File(executableSource.absolutePath + ".dSYM") 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. // 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) Files.copy(executableSource.toPath(), executableDestination.toPath(), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING)
} }

3
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/gradleUtils.kt

@ -37,6 +37,9 @@ internal inline fun <reified T : Task> Project.registerTask(
internal fun Provider<String>.toDir(project: Project): Provider<Directory> = internal fun Provider<String>.toDir(project: Project): Provider<Directory> =
project.layout.dir(map { File(it) }) project.layout.dir(map { File(it) })
internal fun Provider<File>.fileToDir(project: Project): Provider<Directory> =
project.layout.dir(this)
val Project.localPropertiesFile get() = project.rootProject.file("local.properties") val Project.localPropertiesFile get() = project.rootProject.file("local.properties")
fun Project.getLocalProperty(key: String): String? { fun Project.getLocalProperty(key: String): String? {

Loading…
Cancel
Save