Wizard: add shared code for iOS/Android template
fix android
This commit is contained in:
committed by
Kirill Shmakov
parent
66c756ad0f
commit
5ffcaf6508
+15
@@ -2,10 +2,25 @@ package ${package}
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
#if ($sharedPackage)
|
||||
import ${sharedPackage}.Greeting
|
||||
import android.widget.TextView
|
||||
#end
|
||||
|
||||
#if( $sharedPackage )
|
||||
fun greet(): String {
|
||||
return Greeting().greeting()
|
||||
}
|
||||
#end
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
#if( $sharedPackage )
|
||||
val tv: TextView = findViewById(R.id.text_view)
|
||||
tv.text = greet()
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
+16
-14
@@ -1,19 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/main_view"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
android:id="@+id/text_view"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
+13
@@ -1,8 +1,21 @@
|
||||
import SwiftUI
|
||||
#if (${sharedModuleName})
|
||||
import ${sharedModuleName}
|
||||
#end
|
||||
|
||||
#if (${sharedModuleName})
|
||||
func greet() -> String {
|
||||
return Greeting().greeting()
|
||||
}
|
||||
#end
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
#if (${sharedModuleName})
|
||||
Text(greet())
|
||||
#else
|
||||
Text("Hello, World!")
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.tools.projectWizard.core.Writer
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.div
|
||||
import org.jetbrains.kotlin.tools.projectWizard.templates.FileTemplate
|
||||
import org.jetbrains.kotlin.tools.projectWizard.templates.FileTemplateDescriptor
|
||||
import org.jetbrains.kotlin.tools.projectWizard.templates.FileTextDescriptor
|
||||
import org.jetbrains.kotlin.tools.projectWizard.templates.Template
|
||||
import java.io.StringWriter
|
||||
|
||||
@@ -30,9 +31,11 @@ abstract class TemplateEngineService : WizardService {
|
||||
|
||||
fun Writer.writeTemplate(template: FileTemplate): TaskResult<Unit> {
|
||||
val formatter = service<FileFormattingService>()
|
||||
val text = renderTemplate(template.descriptor, template.data).let { text ->
|
||||
formatter.formatFile(text, template.descriptor.relativePath.fileName.toString())
|
||||
val unformattedText = when (val descriptor = template.descriptor) {
|
||||
is FileTemplateDescriptor -> renderTemplate(descriptor, template.data)
|
||||
is FileTextDescriptor -> descriptor.text
|
||||
}
|
||||
val text = formatter.formatFile(unformattedText, template.descriptor.relativePath.fileName.toString())
|
||||
return service<FileSystemWizardService>().createFile(template.rootPath / template.descriptor.relativePath, text)
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -33,6 +33,10 @@ operator fun Path.div(@NonNls other: String): Path =
|
||||
operator fun Path.div(other: Path): Path =
|
||||
resolve(other.toString())
|
||||
|
||||
@JvmName("divNullable")
|
||||
operator fun Path.div(other: Path?): Path =
|
||||
other?.let { resolve(other.toString()) } ?: this
|
||||
|
||||
operator fun @receiver:NonNls String.div(other: Path): Path =
|
||||
Paths.get(this).resolve(other)
|
||||
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ object IOSSinglePlatformModuleConfigurator : SinglePlatformModuleConfigurator,
|
||||
+fileTemplate("$DEFAULT_APP_NAME.xcodeproj" / "project.pbxproj")
|
||||
|
||||
+fileTemplate(DEFAULT_APP_NAME / "AppDelegate.swift")
|
||||
+fileTemplate(DEFAULT_APP_NAME / "ContentView.swift")
|
||||
+fileTemplate(DEFAULT_APP_NAME / "ContentView.swift.vm")
|
||||
+fileTemplate(DEFAULT_APP_NAME / "SceneDelegate.swift")
|
||||
+fileTemplate(DEFAULT_APP_NAME / "Info.plist")
|
||||
|
||||
|
||||
+22
-8
@@ -75,7 +75,13 @@ class ModulesToIRsConverter(
|
||||
module.configurator == AndroidSinglePlatformModuleConfigurator
|
||||
}
|
||||
|
||||
rootModules.mapSequence { module ->
|
||||
allModules.mapSequenceIgnore { module ->
|
||||
forModuleEachDependency(module) { from, to, dependencyType ->
|
||||
with(dependencyType) {
|
||||
runArbitraryTaskBeforeIRsCreated(from, to)
|
||||
}
|
||||
}
|
||||
} andThen rootModules.mapSequence { module ->
|
||||
createBuildFileForModule(
|
||||
module,
|
||||
initialState.copy(parentModuleHasTransitivelySpecifiedKotlinVersion = parentModuleHasKotlinVersion)
|
||||
@@ -112,17 +118,25 @@ class ModulesToIRsConverter(
|
||||
else -> Success(emptyList())
|
||||
}
|
||||
|
||||
private fun <T : Any> forModuleEachDependency(
|
||||
from: Module,
|
||||
action: suspend ComputeContext<NoState>.(from: Module, to: Module, dependencyType: ModuleDependencyType) -> TaskResult<T>
|
||||
): TaskResult<List<T>> {
|
||||
return from.dependencies.mapComputeM { dependency ->
|
||||
val to = data.moduleByPath.getValue(dependency.path)
|
||||
val (dependencyType) = ModuleDependencyType.getPossibleDependencyType(from, to)
|
||||
.toResult { InvalidModuleDependencyError(from, to) }
|
||||
action(from, to, dependencyType)
|
||||
}.sequence()
|
||||
}
|
||||
|
||||
private fun Writer.createSinglePlatformModule(
|
||||
module: Module,
|
||||
configurator: SinglePlatformModuleConfigurator,
|
||||
state: ModulesToIrsState
|
||||
): TaskResult<List<BuildFileIR>> = computeM {
|
||||
val modulePath = calculatePathForModule(module, state.parentPath)
|
||||
val (moduleDependencies) = module.dependencies.mapCompute { dependency ->
|
||||
val to = data.moduleByPath.getValue(dependency.path)
|
||||
val (dependencyType) = ModuleDependencyType.getPossibleDependencyType(module, to)
|
||||
.toResult { InvalidModuleDependencyError(module, to) }
|
||||
|
||||
val (moduleDependencies) = forModuleEachDependency(module) { from, to, dependencyType ->
|
||||
with(dependencyType) {
|
||||
@Suppress("DEPRECATION")
|
||||
with(unsafeSettingWriter) {
|
||||
@@ -134,9 +148,9 @@ class ModulesToIRsConverter(
|
||||
).ensure()
|
||||
}
|
||||
irsToAddToModules.getOrPut(to) { mutableListOf() } += createToIRs(module, to, data).get()
|
||||
createDependencyIrs(module, to, data)
|
||||
createDependencyIrs(module, to, data).asSuccess()
|
||||
}
|
||||
}.sequence().map { it.flatten() }
|
||||
}.map { it.flatten() }
|
||||
mutateProjectStructureByModuleConfigurator(module, modulePath)
|
||||
val buildFileIR = run {
|
||||
if (!configurator.needCreateBuildFile) return@run null
|
||||
|
||||
+64
-12
@@ -14,8 +14,8 @@ import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.ModuleDependencyI
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.isGradle
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModulesToIrConversionData
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.isIOS
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.projectPath
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin
|
||||
@@ -58,6 +58,12 @@ sealed class ModuleDependencyType(
|
||||
): TaskResult<Unit> =
|
||||
UNIT_SUCCESS
|
||||
|
||||
open fun Writer.runArbitraryTaskBeforeIRsCreated(
|
||||
from: Module,
|
||||
to: Module,
|
||||
): TaskResult<Unit> =
|
||||
UNIT_SUCCESS
|
||||
|
||||
open fun Reader.createToIRs(from: Module, to: Module, data: ModulesToIrConversionData): TaskResult<List<BuildSystemIR>> =
|
||||
Success(emptyList())
|
||||
|
||||
@@ -69,7 +75,12 @@ sealed class ModuleDependencyType(
|
||||
object AndroidSinglePlatformToMPP : ModuleDependencyType(
|
||||
from = AndroidSinglePlatformModuleConfigurator::class,
|
||||
to = MppModuleConfigurator::class
|
||||
)
|
||||
) {
|
||||
override fun Writer.runArbitraryTaskBeforeIRsCreated(
|
||||
from: Module,
|
||||
to: Module,
|
||||
): TaskResult<Unit> = addExpectFilesForMppModuleForAndroidAndIos(to)
|
||||
}
|
||||
|
||||
object IOSToMppSinglePlatformToMPP : ModuleDependencyType(
|
||||
from = IOSSinglePlatformModuleConfigurator::class,
|
||||
@@ -83,17 +94,35 @@ sealed class ModuleDependencyType(
|
||||
to: Module,
|
||||
toModulePath: Path,
|
||||
data: ModulesToIrConversionData
|
||||
): TaskResult<Unit> = inContextOfModuleConfigurator(from) {
|
||||
IOSSinglePlatformModuleConfigurator.dependentModule.reference.update {
|
||||
IOSSinglePlatformModuleConfigurator.DependentModuleReference(to).asSuccess()
|
||||
}
|
||||
val dummyFilePath = Defaults.SRC_DIR / "${to.iosTargetSafe()!!.name}Main" / to.configurator.kotlinDirectoryName / "dummyFile.kt"
|
||||
TemplatesPlugin.addFileTemplate.execute(
|
||||
FileTemplate(
|
||||
FileTemplateDescriptor("ios/dummyFile.kt", dummyFilePath),
|
||||
projectPath / toModulePath
|
||||
): TaskResult<Unit> = compute {
|
||||
inContextOfModuleConfigurator(from) {
|
||||
IOSSinglePlatformModuleConfigurator.dependentModule.reference.update {
|
||||
IOSSinglePlatformModuleConfigurator.DependentModuleReference(to).asSuccess()
|
||||
}
|
||||
}.ensure()
|
||||
addDummyFileIfNeeded(to, toModulePath).ensure()
|
||||
}
|
||||
|
||||
override fun Writer.runArbitraryTaskBeforeIRsCreated(
|
||||
from: Module,
|
||||
to: Module,
|
||||
): TaskResult<Unit> = addExpectFilesForMppModuleForAndroidAndIos(to)
|
||||
|
||||
private fun Writer.addDummyFileIfNeeded(
|
||||
to: Module,
|
||||
toModulePath: Path,
|
||||
): TaskResult<Unit> {
|
||||
val needDummyFile = inContextOfModuleConfigurator(to) { MppModuleConfigurator.mppFiles.reference.propertyValue.isEmpty() }
|
||||
return if (needDummyFile) {
|
||||
val dummyFilePath =
|
||||
Defaults.SRC_DIR / "${to.iosTargetSafe()!!.name}Main" / to.configurator.kotlinDirectoryName / "dummyFile.kt"
|
||||
TemplatesPlugin.addFileTemplate.execute(
|
||||
FileTemplate(
|
||||
FileTemplateDescriptor("ios/dummyFile.kt", dummyFilePath),
|
||||
projectPath / toModulePath
|
||||
)
|
||||
)
|
||||
)
|
||||
} else UNIT_SUCCESS
|
||||
}
|
||||
|
||||
override fun additionalAcceptanceChecker(from: Module, to: Module): Boolean =
|
||||
@@ -152,4 +181,27 @@ sealed class ModuleDependencyType(
|
||||
fun isDependencyPossible(from: Module, to: Module): Boolean =
|
||||
getPossibleDependencyType(from, to) != null
|
||||
}
|
||||
}
|
||||
|
||||
private fun Writer.addExpectFilesForMppModuleForAndroidAndIos(mppModule: Module): TaskResult<Unit> {
|
||||
val expectFiles = mppFiles {
|
||||
mppfile("Platform.kt") {
|
||||
`class`("Platform") {
|
||||
expectBody = "val platform: String"
|
||||
actualFor(
|
||||
ModuleSubType.android,
|
||||
actualBody = """actual val platform: String = "Android ${'$'}{android.os.Build.VERSION.SDK_INT}""""
|
||||
)
|
||||
|
||||
actualFor(
|
||||
ModuleSubType.iosArm64, ModuleSubType.iosX64,
|
||||
actualBody =
|
||||
"""actual val platform: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion"""
|
||||
) {
|
||||
import("platform.UIKit.UIDevice")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return inContextOfModuleConfigurator(mppModule) { MppModuleConfigurator.mppFiles.reference.addValues(expectFiles) }
|
||||
}
|
||||
+12
-7
@@ -6,16 +6,21 @@ import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetTy
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
|
||||
// Should be used to create any kind of files in the generated project
|
||||
// Except build files as they will be generated using IR
|
||||
data class FileTemplateDescriptor(@NonNls val templateId: String, val relativePath: Path) {
|
||||
sealed class FileDescriptor {
|
||||
abstract val relativePath: Path
|
||||
}
|
||||
|
||||
data class FileTemplateDescriptor(@NonNls val templateId: String, override val relativePath: Path) : FileDescriptor() {
|
||||
constructor(templateId: String) : this(
|
||||
templateId,
|
||||
Paths.get(templateId).fileName.toString().removeSuffix(".vm").asPath()
|
||||
)
|
||||
}
|
||||
|
||||
data class FileTextDescriptor(val text: String, override val relativePath: Path) : FileDescriptor()
|
||||
|
||||
sealed class FilePath {
|
||||
abstract val sourcesetType: SourcesetType
|
||||
}
|
||||
@@ -23,20 +28,20 @@ sealed class FilePath {
|
||||
data class SrcFilePath(override val sourcesetType: SourcesetType) : FilePath()
|
||||
data class ResourcesFilePath(override val sourcesetType: SourcesetType) : FilePath()
|
||||
|
||||
data class FileTemplateDescriptorWithPath(val descriptor: FileTemplateDescriptor, val path: FilePath)
|
||||
data class FileTemplateDescriptorWithPath(val descriptor: FileDescriptor, val path: FilePath)
|
||||
|
||||
infix fun FileTemplateDescriptor.asResourceOf(sourcesetType: SourcesetType) =
|
||||
infix fun FileDescriptor.asResourceOf(sourcesetType: SourcesetType) =
|
||||
FileTemplateDescriptorWithPath(this, ResourcesFilePath(sourcesetType))
|
||||
|
||||
infix fun FileTemplateDescriptor.asSrcOf(sourcesetType: SourcesetType) =
|
||||
infix fun FileDescriptor.asSrcOf(sourcesetType: SourcesetType) =
|
||||
FileTemplateDescriptorWithPath(this, SrcFilePath(sourcesetType))
|
||||
|
||||
|
||||
data class FileTemplate(
|
||||
val descriptor: FileTemplateDescriptor,
|
||||
val descriptor: FileDescriptor,
|
||||
val rootPath: Path,
|
||||
val data: Map<String, Any?> = emptyMap()
|
||||
) {
|
||||
constructor(descriptor: FileTemplateDescriptor, rootPath: Path, vararg data: Pair<String, Any?>) :
|
||||
constructor(descriptor: FileDescriptor, rootPath: Path, vararg data: Pair<String, Any?>) :
|
||||
this(descriptor, rootPath, data.toMap())
|
||||
}
|
||||
Reference in New Issue
Block a user