diff --git a/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/android/MainActivity.kt.vm b/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/android/MainActivity.kt.vm
index 871b26f5a8b..8b0a86782ef 100644
--- a/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/android/MainActivity.kt.vm
+++ b/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/android/MainActivity.kt.vm
@@ -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
}
}
diff --git a/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/android/activity_main.xml.vm b/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/android/activity_main.xml.vm
index 716af5af1cc..fb2fe57c929 100644
--- a/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/android/activity_main.xml.vm
+++ b/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/android/activity_main.xml.vm
@@ -1,19 +1,21 @@
+ 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">
-
+
\ No newline at end of file
diff --git a/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/ios/singleplatformProject/appName/ContentView.swift b/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/ios/singleplatformProject/appName/ContentView.swift.vm
similarity index 51%
rename from libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/ios/singleplatformProject/appName/ContentView.swift
rename to libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/ios/singleplatformProject/appName/ContentView.swift.vm
index dfe33f9226b..28046806916 100644
--- a/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/ios/singleplatformProject/appName/ContentView.swift
+++ b/libraries/tools/new-project-wizard/resources/org/jetbrains/kotlin/tools/projectWizard/templates/ios/singleplatformProject/appName/ContentView.swift.vm
@@ -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
}
}
diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/service/TemplateEngineService.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/service/TemplateEngineService.kt
index 0d21c91509d..857df026744 100644
--- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/service/TemplateEngineService.kt
+++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/service/TemplateEngineService.kt
@@ -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 {
val formatter = service()
- 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().createFile(template.rootPath / template.descriptor.relativePath, text)
}
}
diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/utils.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/utils.kt
index b5185df00c6..f5d2802fe9d 100644
--- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/utils.kt
+++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/utils.kt
@@ -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)
diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/moduleConfigurators/IOSModuleConfigurator.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/moduleConfigurators/IOSModuleConfigurator.kt
index aa8eaed78d9..aa93f4f0118 100644
--- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/moduleConfigurators/IOSModuleConfigurator.kt
+++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/moduleConfigurators/IOSModuleConfigurator.kt
@@ -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")
diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/plugins/kotlin/ModulesToIRsConverter.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/plugins/kotlin/ModulesToIRsConverter.kt
index a302c870446..bf9b5ad68dc 100644
--- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/plugins/kotlin/ModulesToIRsConverter.kt
+++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/plugins/kotlin/ModulesToIRsConverter.kt
@@ -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 forModuleEachDependency(
+ from: Module,
+ action: suspend ComputeContext.(from: Module, to: Module, dependencyType: ModuleDependencyType) -> TaskResult
+ ): TaskResult> {
+ 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> = 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
diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/settings/buildsystem/ModuleDependency.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/settings/buildsystem/ModuleDependency.kt
index a54b7ec9003..79ef5487698 100644
--- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/settings/buildsystem/ModuleDependency.kt
+++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/settings/buildsystem/ModuleDependency.kt
@@ -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_SUCCESS
+ open fun Writer.runArbitraryTaskBeforeIRsCreated(
+ from: Module,
+ to: Module,
+ ): TaskResult =
+ UNIT_SUCCESS
+
open fun Reader.createToIRs(from: Module, to: Module, data: ModulesToIrConversionData): TaskResult> =
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 = addExpectFilesForMppModuleForAndroidAndIos(to)
+ }
object IOSToMppSinglePlatformToMPP : ModuleDependencyType(
from = IOSSinglePlatformModuleConfigurator::class,
@@ -83,17 +94,35 @@ sealed class ModuleDependencyType(
to: Module,
toModulePath: Path,
data: ModulesToIrConversionData
- ): TaskResult = 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 = 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 = addExpectFilesForMppModuleForAndroidAndIos(to)
+
+ private fun Writer.addDummyFileIfNeeded(
+ to: Module,
+ toModulePath: Path,
+ ): TaskResult {
+ 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 {
+ 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) }
}
\ No newline at end of file
diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/FileTemplateDescriptor.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/FileTemplateDescriptor.kt
index 56534e77e0a..4f1ed9b99b2 100644
--- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/FileTemplateDescriptor.kt
+++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/FileTemplateDescriptor.kt
@@ -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 = emptyMap()
) {
- constructor(descriptor: FileTemplateDescriptor, rootPath: Path, vararg data: Pair) :
+ constructor(descriptor: FileDescriptor, rootPath: Path, vararg data: Pair) :
this(descriptor, rootPath, data.toMap())
}
\ No newline at end of file