Browse Source

COMPOSE-350 Remove old Gradle tasks to launch iOS App (#3753)

Issue https://youtrack.jetbrains.com/issue/COMPOSE-350

Also removed kotlinx-serialization library because we don't need to
parse json output of `xrun simctl` anymore
pull/3777/head
dima.avdeev 1 year ago committed by GitHub
parent
commit
3d4db14b51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      gradle-plugins/build.gradle.kts
  2. 6
      gradle-plugins/compose/build.gradle.kts
  3. 5
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/ExperimentalExtension.kt
  4. 29
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/ExperimentalUiKitApplication.kt
  5. 27
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/ExperimentalUiKitExtension.kt
  6. 59
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/IOSDevices.kt
  7. 54
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/IosDeployConfigurations.kt
  8. 10
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/UiKitConfiguration.kt
  9. 7
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/internal/configureExperimental.kt
  10. 81
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/SimctlListData.kt
  11. 25
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/SimctlUtils.kt
  12. 24
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureExperimentalUikitApplication.kt
  13. 40
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureInstallIosDeployTask.kt
  14. 40
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureInstallXcodeGenTask.kt
  15. 76
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureIosDeployTasks.kt
  16. 42
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configurePackComposeUiKitApplicationForXCodeTask.kt
  17. 66
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureTaskToGenerateXcodeProject.kt
  18. 104
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerConnectedDeviceTasks.kt
  19. 160
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerSimulatorTasks.kt
  20. 66
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/tasks/ExperimentalPackComposeApplicationForXCodeTask.kt
  21. 5
      gradle-plugins/gradle/libs.versions.toml

1
gradle-plugins/build.gradle.kts

@ -3,7 +3,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
plugins {
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.publish.plugin.portal) apply false
alias(libs.plugins.shadow.jar) apply false
alias(libs.plugins.download) apply false

6
gradle-plugins/compose/build.gradle.kts

@ -3,7 +3,6 @@ import de.undercouch.gradle.tasks.download.Download
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.publish.plugin.portal)
id("java-gradle-plugin")
id("maven-publish")
@ -64,14 +63,11 @@ dependencies {
testImplementation(kotlin("gradle-plugin-api"))
embedded(libs.download.task)
embedded(libs.serialization.json)
embedded(libs.serialization.core)
embedded(libs.serialization.core.jvm)
embedded(project(":preview-rpc"))
embedded(project(":jdk-version-probe"))
}
val packagesToRelocate = listOf("de.undercouch", "kotlinx.serialization")
val packagesToRelocate = listOf("de.undercouch")
val shadow = tasks.named<ShadowJar>("shadowJar") {
for (packageToRelocate in packagesToRelocate) {

5
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/ExperimentalExtension.kt

@ -16,9 +16,4 @@ abstract class ExperimentalExtension @Inject constructor(
fun web(action: Action<ExperimentalWebExtension>) {
action.execute(web)
}
val uikit: ExperimentalUiKitExtension = objects.newInstance(ExperimentalUiKitExtension::class.java)
fun uikit(action: Action<ExperimentalUiKitExtension>) {
action.execute(uikit)
}
}

29
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/ExperimentalUiKitApplication.kt

@ -1,29 +0,0 @@
/*
* 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.dsl
import org.gradle.api.Action
import org.gradle.api.model.ObjectFactory
import org.jetbrains.compose.internal.requiredDslProperty
import javax.inject.Inject
@Suppress("unused")
abstract class ExperimentalUiKitApplication @Inject constructor(
val name: String,
val objects: ObjectFactory
) {
var bundleIdPrefix: String by requiredDslProperty("require property [bundleIdPrefix] in uikit.application { ...")
var projectName: String by requiredDslProperty("require property [projectName] in uikit.application { ...")
val configurations: List<UiKitConfiguration> = listOf(
UiKitConfiguration("Debug"),
UiKitConfiguration("Release"),
)
val deployConfigurations: IosDeployConfigurations = objects.newInstance(IosDeployConfigurations::class.java)
fun deployConfigurations(fn: Action<IosDeployConfigurations>) {
fn.execute(deployConfigurations)
}
}

27
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/ExperimentalUiKitExtension.kt

@ -1,27 +0,0 @@
/*
* 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.dsl
import org.gradle.api.Action
import org.gradle.api.model.ObjectFactory
import javax.inject.Inject
abstract class ExperimentalUiKitExtension @Inject constructor(
objectFactory: ObjectFactory
) {
internal var _isApplicationInitialized = false
private set
val application: ExperimentalUiKitApplication by lazy {
_isApplicationInitialized = true
objectFactory.newInstance(ExperimentalUiKitApplication::class.java, "main")
}
fun application(fn: Action<ExperimentalUiKitApplication>) {
fn.execute(application)
}
}

59
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/IOSDevices.kt

@ -1,59 +0,0 @@
/*
* 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.dsl
/**
* iOS device type
* xcrun simctl list devices
*/
@Suppress("unused")
public enum class IOSDevices(val id: String) {
IPHONE_6S("com.apple.CoreSimulator.SimDeviceType.iPhone-6s"),
IPHONE_6S_PLUS("com.apple.CoreSimulator.SimDeviceType.iPhone-6s-Plus"),
IPHONE_SE("com.apple.CoreSimulator.SimDeviceType.iPhone-SE"),
IPHONE_7("com.apple.CoreSimulator.SimDeviceType.iPhone-7"),
IPHONE_7_PLUS("com.apple.CoreSimulator.SimDeviceType.iPhone-7-Plus"),
IPHONE_8("com.apple.CoreSimulator.SimDeviceType.iPhone-8"),
IPHONE_8_PLUS("com.apple.CoreSimulator.SimDeviceType.iPhone-8-Plus"),
IPHONE_X("com.apple.CoreSimulator.SimDeviceType.iPhone-X"),
IPHONE_XS("com.apple.CoreSimulator.SimDeviceType.iPhone-XS"),
IPHONE_XS_MAX("com.apple.CoreSimulator.SimDeviceType.iPhone-XS-Max"),
IPHONE_XR("com.apple.CoreSimulator.SimDeviceType.iPhone-XR"),
IPHONE_11("com.apple.CoreSimulator.SimDeviceType.iPhone-11"),
IPHONE_11_PRO("com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro"),
IPHONE_11_PRO_MAX("com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro-Max"),
IPHONE_SE_2nd_Gen("com.apple.CoreSimulator.SimDeviceType.iPhone-SE--2nd-generation-"),
IPHONE_12_MINI("com.apple.CoreSimulator.SimDeviceType.iPhone-12-mini"),
IPHONE_12("com.apple.CoreSimulator.SimDeviceType.iPhone-12"),
IPHONE_12_PRO("com.apple.CoreSimulator.SimDeviceType.iPhone-12-Pro"),
IPHONE_12_PRO_MAX("com.apple.CoreSimulator.SimDeviceType.iPhone-12-Pro-Max"),
IPHONE_13_PRO("com.apple.CoreSimulator.SimDeviceType.iPhone-13-Pro"),
IPHONE_13_PRO_MAX("com.apple.CoreSimulator.SimDeviceType.iPhone-13-Pro-Max"),
IPHONE_13_MINI("com.apple.CoreSimulator.SimDeviceType.iPhone-13-mini"),
IPHONE_13("com.apple.CoreSimulator.SimDeviceType.iPhone-13"),
IPOD_TOUCH_7th_Gen("com.apple.CoreSimulator.SimDeviceType.iPod-touch--7th-generation-"),
IPAD_MINI_4("com.apple.CoreSimulator.SimDeviceType.iPad-mini-4"),
IPAD_AIR_2("com.apple.CoreSimulator.SimDeviceType.iPad-Air-2"),
IPAD_PRO_9_7_INCH("com.apple.CoreSimulator.SimDeviceType.iPad-Pro--9-7-inch-"),
IPAD_PRO("com.apple.CoreSimulator.SimDeviceType.iPad-Pro"),
IPAD_5th_Gen("com.apple.CoreSimulator.SimDeviceType.iPad--5th-generation-"),
IPAD_PRO_12_9_INCH_2nd_Gen("com.apple.CoreSimulator.SimDeviceType.iPad-Pro--12-9-inch---2nd-generation-"),
IPAD_PRO_10_5_INCH("com.apple.CoreSimulator.SimDeviceType.iPad-Pro--10-5-inch-"),
IPAD_6th_Gen("com.apple.CoreSimulator.SimDeviceType.iPad--6th-generation-"),
IPAD_7th_Gen("com.apple.CoreSimulator.SimDeviceType.iPad--7th-generation-"),
IPAD_PRO_11_INCH("com.apple.CoreSimulator.SimDeviceType.iPad-Pro--11-inch-"),
IPAD_PRO_12_9_INCH_3rd_Gen("com.apple.CoreSimulator.SimDeviceType.iPad-Pro--12-9-inch---3rd-generation-"),
IPAD_PRO_11_INCH_2nd_Gen("com.apple.CoreSimulator.SimDeviceType.iPad-Pro--11-inch---2nd-generation-"),
IPAD_PRO_12_9_INCH_4th_Gen("com.apple.CoreSimulator.SimDeviceType.iPad-Pro--12-9-inch---4th-generation-"),
IPAD_MINI_5th_Gen("com.apple.CoreSimulator.SimDeviceType.iPad-mini--5th-generation-"),
IPAD_AIR_3th_Gen("com.apple.CoreSimulator.SimDeviceType.iPad-Air--3rd-generation-"),
IPAD_8th_Gen("com.apple.CoreSimulator.SimDeviceType.iPad--8th-generation-"),
IPAD_9th_Gen("com.apple.CoreSimulator.SimDeviceType.iPad-9th-generation"),
IPAD_AIR_4th_Gen("com.apple.CoreSimulator.SimDeviceType.iPad-Air--4th-generation-"),
IPAD_PRO_11_INCH_3rd_Gen("com.apple.CoreSimulator.SimDeviceType.iPad-Pro-11-inch-3rd-generation"),
IPAD_12_9_INCH_5th_Gen("com.apple.CoreSimulator.SimDeviceType.iPad-Pro-12-9-inch-5th-generation"),
IPAD_MINI_6th_Gen("com.apple.CoreSimulator.SimDeviceType.iPad-mini-6th-generation"),
}

54
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/IosDeployConfigurations.kt

@ -1,54 +0,0 @@
/*
* 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.dsl
import org.gradle.api.Action
import org.gradle.api.model.ObjectFactory
import java.io.File
import javax.inject.Inject
open class IosDeployConfigurations @Inject constructor(
val objects: ObjectFactory
) {
internal val deployTargets: MutableList<DeployTargetWithId> = mutableListOf()
public fun simulator(id: String, configureSimulator: Action<DeployTarget.Simulator>) {
val currentSimulator = objects.newInstance(DeployTarget.Simulator::class.java)
configureSimulator.execute(currentSimulator)
deployTargets.add(DeployTargetWithId(id, currentSimulator))
}
public fun localFile(id: String, configureLocalFile: Action<DeployTarget.LocalFile>) {
val current = objects.newInstance(DeployTarget.LocalFile::class.java)
configureLocalFile.execute(current)
deployTargets.add(DeployTargetWithId(id, current))
}
public fun connectedDevice(id: String, configureConnectedDevice: Action<DeployTarget.ConnectedDevice>) {
val current = objects.newInstance(DeployTarget.ConnectedDevice::class.java)
configureConnectedDevice.execute(current)
deployTargets.add(DeployTargetWithId(id, current))
}
}
sealed interface DeployTarget {
open class Simulator : DeployTarget {
var device: IOSDevices = IOSDevices.IPHONE_8
}
open class LocalFile : DeployTarget {
var outputFile: File? = null
}
open class ConnectedDevice : DeployTarget {
var teamId: String? = null
}
}
internal class DeployTargetWithId(
val id: String,
val deploy: DeployTarget
)

10
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/dsl/UiKitConfiguration.kt

@ -1,10 +0,0 @@
/*
* 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.dsl
class UiKitConfiguration(
val name: String
)

7
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/internal/configureExperimental.kt

@ -8,7 +8,6 @@ package org.jetbrains.compose.experimental.internal
import org.gradle.api.Project
import org.jetbrains.compose.ComposeExtension
import org.jetbrains.compose.experimental.dsl.ExperimentalExtension
import org.jetbrains.compose.experimental.uikit.internal.configureExperimentalUikitApplication
import org.jetbrains.compose.experimental.web.internal.configureExperimentalWebApplication
import org.jetbrains.compose.web.WebExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
@ -17,12 +16,6 @@ internal fun Project.configureExperimental(
composeExt: ComposeExtension,
experimentalExt: ExperimentalExtension
) {
if (experimentalExt.uikit._isApplicationInitialized) {
val mppExt = project.extensions.findByType(KotlinMultiplatformExtension::class.java)
?: error("'org.jetbrains.kotlin.multiplatform' plugin is required for using 'compose.experimental.uikit {}'")
configureExperimentalUikitApplication(mppExt, experimentalExt.uikit.application)
}
if (experimentalExt.web._isApplicationInitialized) {
val webExt = composeExt.extensions.getByType(WebExtension::class.java)
webExt.targetsToConfigure(project)

81
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/SimctlListData.kt

@ -1,81 +0,0 @@
/*
* 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.
*/
@file:Suppress("unused")
package org.jetbrains.compose.experimental.uikit.internal
import kotlinx.serialization.Serializable
@Serializable
internal class SimctlListData(
val devicetypes: List<DeviceTypeData> = emptyList(),
val runtimes: List<RuntimeData>,
val devices: Map<String, List<DeviceData>>,
val pairs: Map<String, WatchAndPhonePairData> = emptyMap(),
)
@Serializable
internal class DeviceTypeData(
val name: String? = null,
val minRuntimeVersion: Long? = null,
val bundlePath: String? = null,
val maxRuntimeVersion: Long? = null,
val identifier: String? = null,
val productFamily: String? = null
)
@Serializable
internal class RuntimeData(
val name: String? = null,
val bundlePath: String? = null,
val buildversion: String? = null,
val runtimeRoot: String? = null,
val identifier: String,
val version: String,
val isAvailable: Boolean? = null,
val supportedDeviceTypes: List<SupportedDeviceTypeData>
)
@Serializable
internal class SupportedDeviceTypeData(
val bundlePath: String? = null,
val name: String? = null,
val identifier: String,
val productFamily: String? = null
)
@Serializable
internal class DeviceData(
val name: String,
val availabilityError: String? = null,
val dataPath: String? = null,
val dataPathSize: Long? = null,
val logPath: String? = null,
val udid: String,
/**
* Simulator may be unavailable after update Xcode version.
* By default, we think what simulator is available.
*/
val isAvailable: Boolean = true,
val deviceTypeIdentifier: String? = null,
val state: String,
)
internal val DeviceData.booted: Boolean
get() = state == "Booted"
@Serializable
internal class WatchAndPhonePairData(
val watch: DeviceInPairData? = null,
val phone: DeviceInPairData? = null
)
@Serializable
internal class DeviceInPairData(
val name: String? = null,
val udid: String? = null,
val state: String? = null,
)

25
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/SimctlUtils.kt

@ -1,25 +0,0 @@
/*
* 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 kotlinx.serialization.json.Json
import org.jetbrains.compose.internal.utils.MacUtils
import org.jetbrains.compose.experimental.uikit.tasks.AbstractComposeIosTask
val json = Json {
ignoreUnknownKeys = true
}
internal fun AbstractComposeIosTask.getSimctlListData(): SimctlListData {
lateinit var simctlResult: SimctlListData
runExternalTool(
MacUtils.xcrun, listOf("simctl", "list", "--json"),
processStdout = { stdout ->
simctlResult = json.decodeFromString(SimctlListData.serializer(), stdout)
}
)
return simctlResult
}

24
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureExperimentalUikitApplication.kt

@ -1,24 +0,0 @@
/*
* Copyright 2020-2021 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.internal.utils.OS
import org.jetbrains.compose.internal.utils.currentOS
import org.jetbrains.compose.experimental.dsl.ExperimentalUiKitApplication
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
internal fun Project.configureExperimentalUikitApplication(
mppExt: KotlinMultiplatformExtension,
application: ExperimentalUiKitApplication
) {
if (currentOS != OS.MacOS) return
configureIosDeployTasks(
mppExt = mppExt,
application = application,
)
}

40
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureInstallIosDeployTask.kt

@ -1,40 +0,0 @@
/*
* 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.internal.utils.MacUtils
import org.jetbrains.compose.internal.utils.UnixUtils
import org.jetbrains.compose.experimental.uikit.tasks.AbstractComposeIosTask
internal val Project.iosDeployExecutable get() = iosDeploySrc.resolve("build/Release/ios-deploy")
private const val IOS_DEPLOY_GIT = "https://github.com/ios-control/ios-deploy.git"
private const val IOS_DEPLOY_TAG = "1.11.4"
private val Project.iosDeploySrc get() = rootProject.buildDir.resolve("ios-deploy-$IOS_DEPLOY_TAG-src")
internal fun Project.configureInstallIosDeployTask() =
tasks.composeIosTask<AbstractComposeIosTask>("iosInstallIosDeploy") {
onlyIf { !iosDeployExecutable.exists() }
doLast {
iosDeploySrc.deleteRecursively()
runExternalTool(
UnixUtils.git,
listOf(
"clone",
"--depth", "1",
"--branch", IOS_DEPLOY_TAG,
IOS_DEPLOY_GIT,
iosDeploySrc.absolutePath
)
)
runExternalTool(
MacUtils.xcodeBuild,
listOf("-target", "ios-deploy"),
workingDir = iosDeploySrc
)
}
}

40
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureInstallXcodeGenTask.kt

@ -1,40 +0,0 @@
/*
* 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.internal.utils.MacUtils
import org.jetbrains.compose.internal.utils.UnixUtils
import org.jetbrains.compose.experimental.uikit.tasks.AbstractComposeIosTask
internal val Project.xcodeGenExecutable get() = xcodeGenSrc.resolve(".build/apple/Products/Release/xcodegen")
private const val XCODE_GEN_GIT = "https://github.com/yonaskolb/XcodeGen.git"
private const val XCODE_GEN_TAG = "2.26.0"
private val Project.xcodeGenSrc get() = rootProject.buildDir.resolve("xcodegen-$XCODE_GEN_TAG-src")
fun Project.configureInstallXcodeGenTask() =
tasks.composeIosTask<AbstractComposeIosTask>("iosInstallXcodeGen") {
onlyIf { !xcodeGenExecutable.exists() }
doLast {
xcodeGenSrc.deleteRecursively()
runExternalTool(
UnixUtils.git,
listOf(
"clone",
"--depth", "1",
"--branch", XCODE_GEN_TAG,
XCODE_GEN_GIT,
xcodeGenSrc.absolutePath
)
)
runExternalTool(
MacUtils.make,
listOf("build"),
workingDir = xcodeGenSrc
)
}
}

76
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configureIosDeployTasks.kt

@ -1,76 +0,0 @@
/*
* 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.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 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(
mppExt: KotlinMultiplatformExtension,
application: ExperimentalUiKitApplication,
) {
val projectName = application.projectName
val bundleIdPrefix = application.bundleIdPrefix
val taskInstallXcodeGen: TaskProvider<*> = configureInstallXcodeGenTask()
val taskInstallIosDeploy: TaskProvider<*> = configureInstallIosDeployTask()
application.deployConfigurations.deployTargets.forEach { target ->
val id = target.id // .replaceFirstChar { it.uppercase() }
when (target.deploy) {
is DeployTarget.Simulator -> {
registerSimulatorTasks(
mppExt = mppExt,
id = id,
deploy = target.deploy,
projectName = projectName,
bundleIdPrefix = bundleIdPrefix,
taskInstallXcodeGen = taskInstallXcodeGen,
configurations = application.configurations,
)
}
is DeployTarget.LocalFile -> {
TODO("DeployTarget.LocalFile not implemented")
}
is DeployTarget.ConnectedDevice -> {
registerConnectedDeviceTasks(
mppExt = mppExt,
id = id,
deploy = target.deploy,
projectName = projectName,
bundleIdPrefix = bundleIdPrefix,
taskInstallXcodeGen = taskInstallXcodeGen,
taskInstallIosDeploy = taskInstallIosDeploy,
configurations = application.configurations,
)
}
}
}
}
inline fun <reified T : Task> TaskContainer.composeIosTask(
name: String,
args: List<Any> = emptyList(),
noinline configureFn: T.() -> Unit = {}
) = register(name, T::class.java, *args.toTypedArray()).apply {
configure {
it.group = "Compose iOS"
it.configureFn()
}
}

42
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/configurePackComposeUiKitApplicationForXCodeTask.kt

@ -1,42 +0,0 @@
/*
* 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.utils.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)
}

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

@ -1,66 +0,0 @@
/*
* 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.gradle.api.tasks.TaskProvider
import org.jetbrains.compose.experimental.uikit.tasks.AbstractComposeIosTask
internal fun Project.configureTaskToGenerateXcodeProject(
id: String,
projectName: String,
bundleIdPrefix: String,
getTeamId: () -> String? = { null },
taskInstallXcodeGen: TaskProvider<*>,
): TaskProvider<AbstractComposeIosTask> = tasks.composeIosTask<AbstractComposeIosTask>("iosGenerateXcodeProject$id") {
dependsOn(taskInstallXcodeGen)
doLast {
val commonMainResources = file("src/commonMain/resources").absolutePath
val uikitMainResources = file("src/uikitMain/resources").absolutePath
val iosMainResources = file("src/iosMain/resources").absolutePath
val buildIosDir = getBuildIosDir(id)
buildIosDir.mkdirs()
buildIosDir.resolve("project.yml").writeText(
"""
name: $projectName
options:
bundleIdPrefix: $bundleIdPrefix
settings:
${if (getTeamId() != null) "DEVELOPMENT_TEAM: \"${getTeamId()}\"" else ""}
CODE_SIGN_IDENTITY: "iPhone Developer"
CODE_SIGN_STYLE: Automatic
MARKETING_VERSION: "1.0"
CURRENT_PROJECT_VERSION: "4"
SDKROOT: iphoneos
targets:
$projectName:
type: application
platform: iOS
deploymentTarget: "12.0"
info:
path: plists/Ios/Info.plist
properties:
UILaunchStoryboardName: $projectName
settings:
LIBRARY_SEARCH_PATHS: "$(inherited)"
ENABLE_BITCODE: "YES"
ONLY_ACTIVE_ARCH: "NO"
VALID_ARCHS: "arm64"
sources:
- path: $commonMainResources
optional: true
buildPhase: resources
- path: $uikitMainResources
optional: true
buildPhase: resources
- path: $iosMainResources
optional: true
buildPhase: resources
""".trimIndent()
)
runExternalTool(xcodeGenExecutable, emptyList(), workingDir = buildIosDir)
}
}

104
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerConnectedDeviceTasks.kt

@ -1,104 +0,0 @@
/*
* 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.gradle.api.tasks.TaskProvider
import org.jetbrains.compose.internal.utils.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.utils.getLocalProperty
import org.jetbrains.compose.internal.utils.localPropertiesFile
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
fun Project.registerConnectedDeviceTasks(
mppExt: KotlinMultiplatformExtension,
id: String,
deploy: DeployTarget.ConnectedDevice,
projectName: String,
bundleIdPrefix: String,
taskInstallXcodeGen: TaskProvider<*>,
taskInstallIosDeploy: TaskProvider<*>,
configurations: List<UiKitConfiguration>,
) {
val xcodeProjectDir = getBuildIosDir(id).resolve("$projectName.xcodeproj")
val taskGenerateXcodeProject = configureTaskToGenerateXcodeProject(
id = id,
projectName = projectName,
bundleIdPrefix = bundleIdPrefix,
getTeamId = {
deploy.teamId ?: getLocalProperty(TEAM_ID_PROPERTY_KEY)
?: error(
buildString {
appendLine("In local.properties (${localPropertiesFile.absolutePath})")
appendLine("Add property")
appendLine("$TEAM_ID_PROPERTY_KEY=***")
appendLine("Or set teamId in deploy with id: $id")
}
)
},
taskInstallXcodeGen = taskInstallXcodeGen,
)
for (configuration in configurations) {
val configName = configuration.name
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<AbstractComposeIosTask>("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)
runExternalTool(
MacUtils.xcrun,
listOf(
"xcodebuild",
"-scheme", scheme,
"-project", ".",
"-configuration", configName,
"-derivedDataPath", BUILD_DIR_NAME,
"-arch", "arm64",
"-sdk", sdk,
"-allowProvisioningUpdates",
"-allowProvisioningDeviceRegistration",
),
workingDir = xcodeProjectDir
)
}
}
val taskDeploy = tasks.composeIosTask<AbstractComposeIosTask>("iosDeploy$id$configName") {
dependsOn(taskInstallIosDeploy)
dependsOn(taskBuild)
doLast {
runExternalTool(
iosDeployExecutable,
listOf(
"--debug",
"--bundle", iosCompiledAppDir.absolutePath,
)
)
}
}
}
}

160
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/registerSimulatorTasks.kt

@ -1,160 +0,0 @@
/*
* 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.gradle.api.tasks.TaskProvider
import org.jetbrains.compose.internal.utils.Arch
import org.jetbrains.compose.internal.utils.MacUtils
import org.jetbrains.compose.internal.utils.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,
bundleIdPrefix: String,
taskInstallXcodeGen: TaskProvider<*>,
configurations: List<UiKitConfiguration>,
) {
val xcodeProjectDir = getBuildIosDir(id).resolve("$projectName.xcodeproj")
val deviceName = "device-$id"
val taskGenerateXcodeProject = configureTaskToGenerateXcodeProject(
id = id,
projectName = projectName,
bundleIdPrefix = bundleIdPrefix,
taskInstallXcodeGen = taskInstallXcodeGen,
)
val taskSimulatorDeleteUnavailable = tasks.composeIosTask<AbstractComposeIosTask>("iosSimulatorDeleteUnavailable$id") {
doLast {
val device = getSimctlListData().devices.map { it.value }.flatten()
.firstOrNull { device: DeviceData ->
device.name == deviceName && !device.isAvailable
}
if (device != null) {
runExternalTool(
MacUtils.xcrun,
listOf("simctl", "delete", device.udid)
)
}
}
}
val taskCreateSimulator = tasks.composeIosTask<AbstractComposeIosTask>("iosSimulatorCreate$id") {
dependsOn(taskSimulatorDeleteUnavailable)
onlyIf { getSimctlListData().devices.map { it.value }.flatten().none { it.name == deviceName } }
doFirst {
val availableRuntimes = getSimctlListData().runtimes.filter { runtime ->
runtime.supportedDeviceTypes.any { it.identifier == deploy.device.id }
}
val runtime = availableRuntimes.firstOrNull() ?: error("device not found is runtimes")
runExternalTool(
MacUtils.xcrun,
listOf("simctl", "create", deviceName, deploy.device.id, runtime.identifier)
)
}
}
val taskBootSimulator = tasks.composeIosTask<AbstractComposeIosTask>("iosSimulatorBoot$id") {
dependsOn(taskCreateSimulator)
onlyIf {
getSimctlListData().devices.map { it.value }.flatten().any { it.name == deviceName && it.booted.not() }
}
doLast {
val device = getSimctlListData().devices.map { it.value }.flatten().firstOrNull { it.name == deviceName }
?: error("device '$deviceName' not found")
runExternalTool(
MacUtils.xcrun,
listOf("simctl", "boot", device.udid)
)
runExternalTool(
MacUtils.open,
listOf(
"-a", "Simulator",
"--args", "-CurrentDeviceUDID", device.udid
)
)
}
}
val simulatorArch = when (currentArch) {
Arch.X64 -> "x86_64"
Arch.Arm64 -> "arm64"
}
for (configuration in configurations) {
val configName = configuration.name
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<AbstractComposeIosTask>("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)
runExternalTool(
MacUtils.xcrun,
listOf(
"xcodebuild",
"-scheme", scheme,
"-project", ".",
"-configuration", configName,
"-derivedDataPath", BUILD_DIR_NAME,
"-arch", simulatorArch,
"-sdk", sdk
),
workingDir = xcodeProjectDir
)
}
}
val installIosSimulator = tasks.composeIosTask<AbstractComposeIosTask>("iosSimulatorInstall$id$configName") {
dependsOn(taskBuild, taskBootSimulator)
doLast {
val device = getSimctlListData().devices.map { it.value }.flatten()
.firstOrNull { it.name == deviceName && it.booted } ?: error("device $deviceName not booted")
runExternalTool(
MacUtils.xcrun,
listOf("simctl", "install", device.udid, iosCompiledAppDir.absolutePath)
)
}
}
tasks.composeIosTask<AbstractComposeIosTask>("iosDeploy$id$configName") {
dependsOn(installIosSimulator)
doFirst {
val device = getSimctlListData().devices.map { it.value }.flatten()
.firstOrNull { it.name == deviceName && it.booted } ?: error("device $deviceName not booted")
val bundleIdentifier = "$bundleIdPrefix.$projectName"
runExternalTool(
MacUtils.xcrun,
listOf("simctl", "launch", "--console", device.udid, bundleIdentifier)
)
}
}
}
}

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

@ -1,66 +0,0 @@
/*
* Copyright 2020-2021 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.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
import java.io.File
import java.nio.file.Files
import java.nio.file.StandardCopyOption
abstract class ExperimentalPackComposeApplicationForXCodeTask : DefaultTask() {
@get:Input
internal abstract val targetType: Property<UikitTarget>
@get:Input
internal abstract val buildType: Property<NativeBuildType>
@get:InputFile
internal abstract val kotlinBinary: RegularFileProperty
@get:Input
internal abstract val executablePath: Property<String>
@get:OutputDirectory
internal abstract val destinationDir: DirectoryProperty
@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")
val executableDestination = destinationDir.resolve(executablePath.get())
val dsymDestination = File(executableDestination.parentFile.absolutePath + ".dSYM")
for (sourceFile in dsymSource.walk().filter { it.isFile }) {
val relativePath = sourceFile.relativeTo(dsymSource)
val destFile = dsymDestination.resolve(relativePath)
destFile.parentFile.mkdirs()
if (sourceFile.name == executableSource.name) {
sourceFile.copyTo(destFile.resolveSibling(executableDestination.name), true)
} else {
sourceFile.copyTo(destFile, true)
}
}
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)
}
internal enum class UikitTarget(val simulator: Boolean, val targetName: String) {
X64(true, "uikitX64"),
Arm64(false, "uikitArm64")
}
}

5
gradle-plugins/gradle/libs.versions.toml

@ -1,17 +1,12 @@
[versions]
kotlin = "1.9.0"
gradle-download-plugin = "5.5.0"
kotlinx-serialization = "1.2.1"
[libraries]
download-task = { module = "de.undercouch:gradle-download-task", version.ref = "gradle-download-plugin" }
serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" }
serialization-core-jvm = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core-jvm", version.ref = "kotlinx-serialization" }
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
shadow-jar = "com.github.johnrengelman.shadow:8.1.1"
download = { id = "de.undercouch.download", version.ref = "gradle-download-plugin" }
publish-plugin-portal = "com.gradle.plugin-publish:1.2.1"

Loading…
Cancel
Save