Browse Source
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` anymorepull/3777/head
dima.avdeev
1 year ago
committed by
GitHub
21 changed files with 1 additions and 926 deletions
@ -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) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
||||
|
@ -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"), |
||||
} |
@ -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 |
||||
) |
@ -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 |
||||
) |
@ -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, |
||||
) |
@ -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 |
||||
} |
@ -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, |
||||
) |
||||
} |
@ -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 |
||||
) |
||||
} |
||||
} |
@ -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 |
||||
) |
||||
} |
||||
} |
@ -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() |
||||
} |
||||
} |
@ -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) |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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, |
||||
) |
||||
) |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -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) |
||||
) |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -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") |
||||
} |
||||
} |
@ -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…
Reference in new issue