Wizard: introduce Compose MPP template

This commit is contained in:
Ilya Kirillov
2020-10-09 21:35:59 +03:00
parent 7422690ea1
commit 1cc007fcbf
37 changed files with 783 additions and 66 deletions
@@ -94,6 +94,7 @@ private val ProjectTemplate.icon: Icon
MultiplatformMobileLibraryProjectTemplate -> KotlinIcons.Wizard.MULTIPLATFORM_MOBILE_LIBRARY
NodeJsApplicationProjectTemplate -> KotlinIcons.Wizard.NODE_JS
ComposeDesktopApplicationProjectTemplate -> KotlinIcons.Wizard.COMPOSE
ComposeMultiplatformApplicationProjectTemplate -> KotlinIcons.Wizard.COMPOSE
}
class TemplateDescriptionComponent : Component() {
@@ -29,7 +29,7 @@ abstract class AbstractAsyncStackTraceTest : KotlinDescriptorTestCaseWithSteppin
val frameProxy = this.frameProxy
if (frameProxy != null) {
try {
val sem = frameProxy.location().isPreFlight()
/* val sem = frameProxy.location().isPreFlight()
val coroutineInfoData = if (sem.isCoroutineFound())
CoroutineFrameBuilder.lookupContinuation(this, frameProxy, sem)?.coroutineInfoData
else
@@ -38,7 +38,7 @@ abstract class AbstractAsyncStackTraceTest : KotlinDescriptorTestCaseWithSteppin
print(renderAsyncStackTrace(coroutineInfoData.stackTrace), ProcessOutputTypes.SYSTEM)
} else {
println("No async stack trace available", ProcessOutputTypes.SYSTEM)
}
}*/
} catch (e: Throwable) {
val stackTrace = e.stackTraceAsString()
System.err.println("Exception occurred on calculating async stack traces: $stackTrace")
@@ -0,0 +1,32 @@
plugins {
id 'org.jetbrains.compose' version '0.1.0-build63'
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
group = 'me.user'
version = '1.0-SNAPSHOT'
repositories {
google()
}
dependencies {
implementation project(':common')
}
android {
compileSdkVersion 29
defaultConfig {
applicationId 'me.user.android'
minSdkVersion 24
targetSdkVersion 29
versionCode 1
versionName '1.0'
}
buildTypes {
'release' {
minifyEnabled false
}
}
}
@@ -0,0 +1,32 @@
plugins {
id("org.jetbrains.compose") version "0.1.0-build63"
id("com.android.application")
kotlin("android")
}
group = "me.user"
version = "1.0-SNAPSHOT"
repositories {
google()
}
dependencies {
implementation(project(":common"))
}
android {
compileSdkVersion(29)
defaultConfig {
applicationId = "me.user.android"
minSdkVersion(24)
targetSdkVersion(29)
versionCode = 1
versionName = "1.0"
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
}
@@ -0,0 +1,24 @@
buildscript {
repositories {
gradlePluginPortal()
jcenter()
google()
maven { url 'KOTLIN_REPO' }
}
dependencies {
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin:KOTLIN_VERSION')
classpath('com.android.tools.build:gradle:4.0.1')
}
}
group = 'me.user'
version = '1.0-SNAPSHOT'
allprojects {
repositories {
jcenter()
mavenCentral()
maven { url 'KOTLIN_REPO' }
maven { url 'https://maven.pkg.jetbrains.space/public/p/compose/dev' }
}
}
@@ -0,0 +1,24 @@
buildscript {
repositories {
gradlePluginPortal()
jcenter()
google()
maven { url = uri("KOTLIN_REPO") }
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:KOTLIN_VERSION")
classpath("com.android.tools.build:gradle:4.0.1")
}
}
group = "me.user"
version = "1.0-SNAPSHOT"
allprojects {
repositories {
jcenter()
mavenCentral()
maven { url = uri("KOTLIN_REPO") }
maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev") }
}
}
@@ -0,0 +1,60 @@
import org.jetbrains.compose.compose
plugins {
id 'org.jetbrains.kotlin.multiplatform'
id 'org.jetbrains.compose' version '0.1.0-build63'
id 'com.android.library'
id 'kotlin-android-extensions'
}
group = 'me.user'
version = '1.0-SNAPSHOT'
repositories {
google()
}
kotlin {
android()
jvm('desktop') {
compilations.all {
kotlinOptions.jvmTarget = '1.8'
}
}
sourceSets {
commonMain {
dependencies {
api compose.runtime
api compose.foundation
api compose.material
}
}
commonTest {
}
androidMain {
dependencies {
api 'androidx.appcompat:appcompat:1.2.0'
api 'androidx.core:core-ktx:1.3.1'
}
}
androidTest {
}
desktopMain {
}
desktopTest {
}
}
}
android {
compileSdkVersion 29
sourceSets.main.manifest.srcFile('src/androidMain/AndroidManifest.xml')
defaultConfig {
minSdkVersion 24
targetSdkVersion 29
}
}
@@ -0,0 +1,52 @@
import org.jetbrains.compose.compose
plugins {
kotlin("multiplatform")
id("org.jetbrains.compose") version "0.1.0-build63"
id("com.android.library")
id("kotlin-android-extensions")
}
group = "me.user"
version = "1.0-SNAPSHOT"
repositories {
google()
}
kotlin {
android()
jvm("desktop") {
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
}
sourceSets {
val commonMain by getting {
dependencies {
api(compose.runtime)
api(compose.foundation)
api(compose.material)
}
}
val commonTest by getting
val androidMain by getting {
dependencies {
api("androidx.appcompat:appcompat:1.2.0")
api("androidx.core:core-ktx:1.3.1")
}
}
val androidTest by getting
val desktopMain by getting
val desktopTest by getting
}
}
android {
compileSdkVersion(29)
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdkVersion(24)
targetSdkVersion(29)
}
}
@@ -0,0 +1,27 @@
import org.jetbrains.compose.compose
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'org.jetbrains.compose' version '0.1.0-build63'
id 'application'
}
group = 'me.user'
version = '1.0-SNAPSHOT'
dependencies {
implementation project(':common')
implementation compose.desktop.all
}
compileKotlin {
kotlinOptions.jvmTarget = '1.8'
}
compileTestKotlin {
kotlinOptions.jvmTarget = '1.8'
}
application {
mainClassName = 'MainKt'
}
@@ -0,0 +1,24 @@
import org.jetbrains.compose.compose
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm")
id("org.jetbrains.compose") version "0.1.0-build63"
application
}
group = "me.user"
version = "1.0-SNAPSHOT"
dependencies {
implementation(project(":common"))
implementation(compose.desktop.all)
}
tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "1.8"
}
application {
mainClassName = "MainKt"
}
@@ -0,0 +1,17 @@
pluginManagement {
repositories {
google()
jcenter()
gradlePluginPortal()
mavenCentral()
maven { url 'KOTLIN_REPO' }
maven { url 'https://maven.pkg.jetbrains.space/public/p/compose/dev' }
}
}
rootProject.name = 'composeMultiplatformApplication'
include(':android')
include(':desktop')
include(':common')
@@ -0,0 +1,17 @@
pluginManagement {
repositories {
google()
jcenter()
gradlePluginPortal()
mavenCentral()
maven { url = uri("KOTLIN_REPO") }
maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev") }
}
}
rootProject.name = "composeMultiplatformApplication"
include(":android")
include(":desktop")
include(":common")
@@ -151,17 +151,24 @@ project.template.mpp.mobile.lib.description=Library that supports sharing code b
project.template.compose.desktop.title=Desktop
project.template.compose.desktop.description=A Compose application targeting the desktop JVM platform (Windows, Linux, macOS)
project.template.compose.multiplatform.title=Multiplatform
project.template.compose.multiplatform.description=Multiplatform (JVM/Windows, JVM/Linux, JVM/MacOS, Android) Compose project with shared code in common modules
module.template.console.jvm.title=Console Application
module.template.console.jvm.description=Simple "Hello World!" Kotlin/JVM application that works in the console
module.template.compose.desktop.title=Compose Desktop Application
module.template.compose.desktop.description=A Compose application targeting the desktop JVM platform
module.template.mpp.mobile.title=Mobile Application
module.template.mpp.mobile.description=Mobile applications for iOS and Android with Kotlin Multiplatform Mobile, which supports sharing common code between platforms.
module.template.compose.desktop.title=Compose Desktop Application
module.template.compose.desktop.description=A Compose application targeting the desktop JVM platform
module.template.compose.mpp.title=Compose MPP Module
module.template.compose.mpp.description=Compose MPP Module
module.template.ktor.server.title=Ktor server
module.template.ktor.server.description=Configurable Ktor web server for building web applications
module.template.ktor.server.setting.engine=Ktor server engine
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="${package}">
<application
android:allowBackup="false"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,20 @@
#if ($package)
package ${package}
#end
#if ($sharedPackage)
import ${sharedPackage}.App
#end
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.ui.platform.setContent
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
#if ($sharedPackage)
setContent {
App()
}
#end
}
}
@@ -0,0 +1,21 @@
#if ($package)
package ${package}
#end
#set( $dollar = "${" )
import androidx.compose.foundation.Text
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.*
@Composable
fun App() {
var text by remember { mutableStateOf("Hello, World!") }
MaterialTheme {
Button(onClick = {
text = "Hello, ${dollar}getPlatformName()}"
}) {
Text(text)
}
}
}
@@ -0,0 +1,11 @@
#if ($package)
package ${package}
#end
#if ($sharedPackage)
import ${sharedPackage}.App
#end
import androidx.compose.desktop.Window
fun main() = Window {
App()
}
@@ -0,0 +1,18 @@
#set( $dollar = "${" )
import androidx.compose.foundation.Text
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.*
@Composable
fun App() {
var text by remember { mutableStateOf("Hello, World!") }
MaterialTheme {
Button(onClick = {
text = "Hello, ${dollar}getPlatformName()}"
}) {
Text(text)
}
}
}
@@ -20,6 +20,7 @@ object Versions {
val ANDROID_MATERIAL = version("1.2.1")
val ANDROIDX_APPCOMPAT = version("1.2.0")
val ANDROIDX_CONSTRAINTLAYOUT = version("2.0.2")
val ANDROIDX_KTX = version("1.3.1")
}
object KOTLINX {
@@ -11,6 +11,8 @@ import org.jetbrains.kotlin.tools.projectWizard.core.service.SettingSavingWizard
import org.jetbrains.kotlin.tools.projectWizard.core.service.WizardService
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.allIRModules
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.path
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.allModulesPaths
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.ModuleReference
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.path
@@ -243,13 +245,13 @@ fun Reader.getUnspecifiedSettings(phases: Set<GenerationPhase>): List<AnySetting
val provided = pluginSettings.map(PluginSetting<*, *>::path).toSet()
return required.filterNot { it.path in provided }
}
fun Reader.moduleByReference(reference: ModuleReference): Module = when (reference) {
is ModuleReference.ByModule -> reference.module
is ModuleReference.ByPath -> allIRModules.first { it.originalModule.path == reference.path }.originalModule
}
typealias Reader = Context.Reader
typealias Writer = Context.Writer
typealias SettingsWriter = Context.SettingsWriter
@@ -181,8 +181,6 @@ fun MultiplatformModulesStructureIR.updateSourceSets(
return copy(modules = newModules)
}
data class SingleplatformModulesStructureWithSingleModuleIR(
val module: SingleplatformModuleIR,
override val irs: PersistentList<BuildSystemIR>
@@ -27,7 +27,7 @@ data class ModuleDependencyIR(
override fun withDependencyType(type: DependencyType): DependencyIR = copy(dependencyType = type)
override fun BuildFilePrinter.render() = when (this) {
is GradlePrinter -> call(dependencyType.getGradleName()) {
is GradlePrinter -> call(dependencyType.getGradleName(DependencyKind.implementation)) {
call("project", forceBrackets = true) {
+path.parts.joinToString(separator = "") { ":$it" }.quotified
}
@@ -64,20 +64,22 @@ fun SourcesetType.toDependencyType() = when (this) {
SourcesetType.test -> DependencyType.TEST
}
fun DependencyType.getGradleName(type: String = "implementation") = when (this) {
DependencyType.MAIN -> type
DependencyType.TEST -> "test${type.capitalize(Locale.US)}"
fun DependencyType.getGradleName(kind: DependencyKind) = when (this) {
DependencyType.MAIN -> kind.text
DependencyType.TEST -> "test${kind.text.capitalize(Locale.US)}"
}
data class ArtifactBasedLibraryDependencyIR(
override val artifact: LibraryArtifact,
override val version: Version,
override val dependencyType: DependencyType,
val dependencyKind: String = "implementation"
val dependencyKind: DependencyKind = DependencyKind.implementation
) : LibraryDependencyIR {
override fun withDependencyType(type: DependencyType): ArtifactBasedLibraryDependencyIR =
copy(dependencyType = type)
fun withDependencyKind(kind: DependencyKind): ArtifactBasedLibraryDependencyIR = copy(dependencyKind = kind)
override fun BuildFilePrinter.render() = when (this) {
is GradlePrinter -> call(dependencyType.getGradleName(dependencyKind)) {
with(artifact) {
@@ -126,7 +128,7 @@ abstract class KotlinLibraryDependencyIR(
override fun BuildFilePrinter.render() {
when (this) {
is GradlePrinter -> call(dependencyType.getGradleName()) {
is GradlePrinter -> call(dependencyType.getGradleName(DependencyKind.implementation)) {
if (GradlePrinter.GradleDsl.KOTLIN == dsl || isInMppModule) {
+"kotlin("
+artifactName.quotified
@@ -170,17 +172,25 @@ data class KotlinArbitraryDependencyIR(
data class CustomGradleDependencyDependencyIR(
val dependency: String,
override val dependencyType: DependencyType
override val dependencyType: DependencyType,
val dependencyKind: DependencyKind
) : DependencyIR, GradleIR {
override fun withDependencyType(type: DependencyType): CustomGradleDependencyDependencyIR = copy(dependencyType = type)
override fun GradlePrinter.renderGradle() {
call(dependencyType.getGradleName()) {
call(dependencyType.getGradleName(dependencyKind)) {
+dependency
}
}
}
enum class DependencyKind(val text: String) {
implementation("implementation"),
api("api"),
runtimeOnly("runtimeOnly"),
compileOnly("compileOnly")
}
enum class StdlibType(val artifact: String) {
StdlibJdk7("stdlib-jdk7"),
StdlibJdk8("stdlib-jdk8"),
@@ -21,7 +21,6 @@ inline fun <I : IrsOwner> I.withoutIrs(filterNot: (BuildSystemIR) -> Boolean): I
withReplacedIrs(irs = this.irs.filterNot(filterNot).toPersistentList()) as I
inline fun <reified I : BuildSystemIR> IrsOwner.irsOfType(): List<I> =
irs.filterIsInstance<I>().let { irs ->
if (irs.all { it is BuildSystemIRWithPriority })
@@ -84,14 +84,9 @@ interface AndroidModuleConfigurator : ModuleConfigurator,
reader: Reader,
configurationData: ModulesToIrConversionData,
module: Module
): List<BuildSystemIR> =
buildList {
+ArtifactBasedLibraryDependencyIR(
MavenArtifact(DefaultRepository.GOOGLE, "com.google.android.material", "material"),
version = Versions.ANDROID.ANDROID_MATERIAL,
dependencyType = DependencyType.MAIN
)
}
): List<BuildSystemIR> = buildList {
+DEPENDENCIES.MATERIAL
}
override fun createStdlibType(configurationData: ModulesToIrConversionData, module: Module): StdlibType? =
@@ -119,6 +114,15 @@ interface AndroidModuleConfigurator : ModuleConfigurator,
)
}
object DEPENDENCIES {
const val KOTLIN_ANDROID_EXTENSIONS_NAME = "kotlin-android-extensions"
val MATERIAL = ArtifactBasedLibraryDependencyIR(
MavenArtifact(DefaultRepository.GOOGLE, "com.google.android.material", "material"),
version = Versions.ANDROID.ANDROID_MATERIAL,
dependencyType = DependencyType.MAIN
)
}
companion object {
fun createRepositories(kotlinVersion: WizardKotlinVersion) = buildList<Repository> {
+DefaultRepository.GRADLE_PLUGIN_PORTAL
@@ -185,11 +189,6 @@ object AndroidTargetConfigurator : TargetConfigurator,
buildList {
+super<ModuleConfiguratorWithTests>.createModuleIRs(reader, configurationData, module)
+super<AndroidModuleConfigurator>.createModuleIRs(reader, configurationData, module)
+ArtifactBasedLibraryDependencyIR(
MavenArtifact(DefaultRepository.MAVEN_CENTRAL, "junit", "junit"),
version = Versions.JUNIT,
dependencyType = DependencyType.TEST
)
}
override fun createBuildFileIRs(reader: Reader, configurationData: ModulesToIrConversionData, module: Module): List<BuildSystemIR> =
@@ -10,6 +10,7 @@ import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.tools.projectWizard.KotlinNewProjectWizardBundle
import org.jetbrains.kotlin.tools.projectWizard.Versions
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.core.entity.properties.ModuleConfiguratorProperty
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.AndroidConfigIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.BuildScriptDependencyIR
@@ -22,7 +23,6 @@ import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModulesToIrConver
import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.settings.javaPackage
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
import org.jetbrains.kotlin.tools.projectWizard.templates.FileTemplate
import java.nio.file.Path
@@ -85,24 +85,15 @@ object AndroidSinglePlatformModuleConfigurator :
priority = 2
)
override fun createModuleIRs(
reader: Reader,
configurationData: ModulesToIrConversionData,
module: Module
) = buildList<BuildSystemIR> {
): List<BuildSystemIR> = buildList {
+super<AndroidModuleConfigurator>.createModuleIRs(reader, configurationData, module)
+ArtifactBasedLibraryDependencyIR(
MavenArtifact(DefaultRepository.GOOGLE, "androidx.appcompat", "appcompat"),
version = Versions.ANDROID.ANDROIDX_APPCOMPAT,
dependencyType = DependencyType.MAIN
)
+ArtifactBasedLibraryDependencyIR(
MavenArtifact(DefaultRepository.GOOGLE, "androidx.constraintlayout", "constraintlayout"),
version = Versions.ANDROID.ANDROIDX_CONSTRAINTLAYOUT,
dependencyType = DependencyType.MAIN
)
+DEPENDENCIES.APP_COMPAT
+DEPENDENCIES.CONSTRAINT_LAYOUT
}
override fun Writer.runArbitraryTask(
@@ -121,6 +112,7 @@ object AndroidSinglePlatformModuleConfigurator :
"package" to javaPackage.asCodePackage(),
"sharedPackage" to sharedPackage?.asCodePackage()
)
TemplatesPlugin.addFileTemplates.execute(
listOf(
FileTemplate(AndroidModuleConfigurator.FileTemplateDescriptors.activityMainXml, modulePath, settings),
@@ -135,4 +127,19 @@ object AndroidSinglePlatformModuleConfigurator :
override fun Reader.createAndroidPlugin(module: Module): AndroidGradlePlugin =
AndroidGradlePlugin.APPLICATION
object DEPENDENCIES {
val CONSTRAINT_LAYOUT = ArtifactBasedLibraryDependencyIR(
MavenArtifact(DefaultRepository.GOOGLE, "androidx.constraintlayout", "constraintlayout"),
version = Versions.ANDROID.ANDROIDX_CONSTRAINTLAYOUT,
dependencyType = DependencyType.MAIN
)
val APP_COMPAT = ArtifactBasedLibraryDependencyIR(
MavenArtifact(DefaultRepository.GOOGLE, "androidx.appcompat", "appcompat"),
version = Versions.ANDROID.ANDROIDX_APPCOMPAT,
dependencyType = DependencyType.MAIN
)
}
}
@@ -129,7 +129,7 @@ enum class KotlinTestFramework(
MavenArtifact(DefaultRepository.MAVEN_CENTRAL, "org.junit.jupiter", "junit-jupiter-engine"),
version = Versions.JUNIT5,
dependencyType = DependencyType.TEST,
dependencyKind = "runtimeOnly"
dependencyKind = DependencyKind.runtimeOnly
),
)
),
@@ -108,7 +108,7 @@ val Reader.projectName
get() = StructurePlugin.name.settingValue
fun Writer.pomIR() = PomIR(
fun Reader.pomIR() = PomIR(
artifactId = StructurePlugin.artifactId.settingValue,
groupId = StructurePlugin.groupId.settingValue,
version = Version.fromString(StructurePlugin.version.settingValue)
@@ -285,6 +285,9 @@ class ModulesToIRsConverter(
): TaskResult<Unit> = with(module.configurator) {
compute {
rootBuildFileIrs += createRootBuildFileIrs(data)
module.template?.let { template ->
rootBuildFileIrs += with(template) { createRootBuildFileIrs() }
}
runArbitraryTask(data, module, modulePath).ensure()
TemplatesPlugin.addFileTemplates.execute(createTemplates(data, module, modulePath)).ensure()
if (this@with is GradleModuleConfigurator) {
@@ -321,5 +324,8 @@ class ModulesToIRsConverter(
}
addIfNotNull(kotlinPlugin)
+with(module.configurator) { createBuildFileIRs(this@createBuildFileIRs, data, module) }
.let {
module.template?.run { updateBuildFileIRs(it) } ?: it
}
}
}
@@ -2,9 +2,11 @@ package org.jetbrains.kotlin.tools.projectWizard.plugins.templates
import org.jetbrains.kotlin.tools.projectWizard.core.Context
import org.jetbrains.kotlin.tools.projectWizard.core.PluginSettingsOwner
import org.jetbrains.kotlin.tools.projectWizard.core.andThen
import org.jetbrains.kotlin.tools.projectWizard.core.entity.PipelineTask
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.templates.compose.ComposeJvmDesktopTemplate
import org.jetbrains.kotlin.tools.projectWizard.templates.compose.ComposeMppModuleTemplate
class ComposeTemplatesPlugin(context: Context) : TemplatePlugin(context) {
override val path = pluginPath
@@ -19,7 +21,9 @@ class ComposeTemplatesPlugin(context: Context) : TemplatePlugin(context) {
val addTemplate by pipelineTask(GenerationPhase.PREPARE) {
withAction {
TemplatesPlugin.addTemplate.execute(ComposeJvmDesktopTemplate())
TemplatesPlugin.addTemplate.execute(ComposeJvmDesktopTemplate()) andThen
TemplatesPlugin.addTemplate.execute(ComposeJvmDesktopTemplate()) andThen
TemplatesPlugin.addTemplate.execute(ComposeMppModuleTemplate())
}
}
}
@@ -139,12 +139,12 @@ class TemplatesPlugin(context: Context) : Plugin(context) {
putAll(interceptionPointSettings.mapKeys { it.key.name })
putAll(defaultSettings(module))
}
return with(template) { getFileTemplates(module) }.mapNotNull { (fileTemplateDescriptor, filePath) ->
return with(template) { getFileTemplates(module) }.mapNotNull { (fileTemplateDescriptor, filePath, settings) ->
val path = generatePathForFileTemplate(module, filePath) ?: return@mapNotNull null
val fileTemplate = FileTemplate(
fileTemplateDescriptor,
module.path / path,
allSettings
allSettings + settings
)
with(templateEngine) { writeTemplate(fileTemplate) }
}.sequenceIgnore()
@@ -18,7 +18,9 @@ import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.templates.*
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.JvmSinglePlatformModuleConfigurator
import org.jetbrains.kotlin.tools.projectWizard.templates.compose.ComposeAndroidTemplate
import org.jetbrains.kotlin.tools.projectWizard.templates.compose.ComposeJvmDesktopTemplate
import org.jetbrains.kotlin.tools.projectWizard.templates.compose.ComposeMppModuleTemplate
import org.jetbrains.kotlin.tools.projectWizard.templates.mpp.MobileMppTemplate
sealed class ProjectTemplate : DisplayableSettingItem {
@@ -84,6 +86,7 @@ sealed class ProjectTemplate : DisplayableSettingItem {
FullStackWebApplicationProjectTemplate,
NodeJsApplicationProjectTemplate,
ComposeDesktopApplicationProjectTemplate,
ComposeMultiplatformApplicationProjectTemplate,
)
fun byId(id: String): ProjectTemplate? = ALL.firstOrNull {
@@ -473,4 +476,67 @@ object ComposeDesktopApplicationProjectTemplate : ProjectTemplate() {
}
)
)
}
object ComposeMultiplatformApplicationProjectTemplate : ProjectTemplate() {
override val title = KotlinNewProjectWizardBundle.message("project.template.compose.multiplatform.title")
override val description = KotlinNewProjectWizardBundle.message("project.template.compose.multiplatform.description")
override val id = "composeMultiplatformApplication"
@NonNls
override val suggestedProjectName = "myComposeMultiplatformApplication"
override val projectKind = ProjectKind.COMPOSE
override val setsModules: List<Module>
get() = buildList {
val common = MultiplatformModule(
"common",
template = ComposeMppModuleTemplate(),
listOf(
ModuleType.common.createDefaultTarget().withConfiguratorSettings(CommonTargetConfigurator) {
ModuleConfiguratorWithTests.testFramework withValue KotlinTestFramework.NONE
},
Module(
"android",
AndroidTargetConfigurator,
template = null,
sourcesets = createDefaultSourcesets(),
subModules = emptyList()
).withConfiguratorSettings(AndroidTargetConfigurator) {
configurator.androidPlugin withValue AndroidGradlePlugin.LIBRARY
ModuleConfiguratorWithTests.testFramework withValue KotlinTestFramework.NONE
},
Module(
"desktop",
JvmTargetConfigurator,
template = null,
sourcesets = createDefaultSourcesets(),
subModules = emptyList()
).withConfiguratorSettings(JvmTargetConfigurator) {
ModuleConfiguratorWithTests.testFramework withValue KotlinTestFramework.NONE
}
)
)
+Module(
"android",
AndroidSinglePlatformModuleConfigurator,
template = ComposeAndroidTemplate(),
sourcesets = createDefaultSourcesets(),
subModules = emptyList(),
dependencies = mutableListOf(ModuleReference.ByModule(common))
).withConfiguratorSettings(AndroidSinglePlatformModuleConfigurator) {
ModuleConfiguratorWithTests.testFramework withValue KotlinTestFramework.NONE
}
+Module(
"desktop",
JvmSinglePlatformModuleConfigurator,
template = ComposeJvmDesktopTemplate(),
sourcesets = createDefaultSourcesets(),
subModules = emptyList(),
dependencies = mutableListOf(ModuleReference.ByModule(common))
).withConfiguratorSettings(JvmSinglePlatformModuleConfigurator) {
ModuleConfiguratorWithTests.testFramework withValue KotlinTestFramework.NONE
}
+common
}
}
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin.tools.projectWizard.settings
import org.jetbrains.kotlin.tools.projectWizard.core.buildList
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.PomIR
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.TargetConfigurator
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
import java.nio.file.Paths
@@ -20,5 +21,10 @@ class JavaPackage(val parts: List<String>) {
}
}
fun Module.javaPackage(pomIr: PomIR) =
JavaPackage.fromCodeRepresentation(pomIr.groupId, name)
fun Module.javaPackage(pomIr: PomIR): JavaPackage {
val moduleName = when (configurator) {
is TargetConfigurator -> parent!!.name
else -> name
}
return JavaPackage.fromCodeRepresentation(pomIr.groupId, moduleName)
}
@@ -28,7 +28,11 @@ sealed class FilePath {
data class SrcFilePath(override val sourcesetType: SourcesetType) : FilePath()
data class ResourcesFilePath(override val sourcesetType: SourcesetType) : FilePath()
data class FileTemplateDescriptorWithPath(val descriptor: FileDescriptor, val path: FilePath)
data class FileTemplateDescriptorWithPath(
val descriptor: FileDescriptor,
val path: FilePath,
val data: Map<String, Any> = emptyMap(),
)
infix fun FileDescriptor.asResourceOf(sourcesetType: SourcesetType) =
FileTemplateDescriptorWithPath(this, ResourcesFilePath(sourcesetType))
@@ -36,6 +40,9 @@ infix fun FileDescriptor.asResourceOf(sourcesetType: SourcesetType) =
infix fun FileDescriptor.asSrcOf(sourcesetType: SourcesetType) =
FileTemplateDescriptorWithPath(this, SrcFilePath(sourcesetType))
infix fun FileTemplateDescriptorWithPath.withSettings(setting: Pair<String, String>) =
copy(data = data + setting)
data class FileTemplate(
val descriptor: FileDescriptor,
@@ -0,0 +1,105 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.tools.projectWizard.templates.compose
import kotlinx.collections.immutable.toPersistentList
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.tools.projectWizard.KotlinNewProjectWizardBundle
import org.jetbrains.kotlin.tools.projectWizard.Versions
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.GradleImportIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.irsList
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.TargetConfigurationIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.addWithJavaIntoJvmTarget
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.AndroidModuleConfigurator
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.AndroidSinglePlatformModuleConfigurator
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.inContextOfModuleConfigurator
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.moduleType
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemPlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
import org.jetbrains.kotlin.tools.projectWizard.plugins.pomIR
import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin
import org.jetbrains.kotlin.tools.projectWizard.settings.JavaPackage
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.settings.javaPackage
import org.jetbrains.kotlin.tools.projectWizard.templates.FileTemplateDescriptor
import org.jetbrains.kotlin.tools.projectWizard.templates.FileTemplateDescriptorWithPath
import org.jetbrains.kotlin.tools.projectWizard.templates.Template
import org.jetbrains.kotlin.tools.projectWizard.templates.asSrcOf
class ComposeAndroidTemplate : Template() {
@NonNls
override val id: String = "composeAndroid"
override val title: String = KotlinNewProjectWizardBundle.message("module.template.compose.desktop.title")
override val description: String = KotlinNewProjectWizardBundle.message("module.template.compose.desktop.description")
override fun isSupportedByModuleType(module: Module): Boolean =
module.configurator.moduleType == ModuleType.android
override fun isApplicableTo(reader: Reader, module: Module): Boolean =
module.kind == ModuleKind.singleplatformAndroid
override fun Writer.getIrsToAddToBuildFile(
module: ModuleIR
) = irsList {
+GradleOnlyPluginByNameIR("org.jetbrains.compose", version = Versions.JETBRAINS_COMPOSE)
+RepositoryIR(Repositories.JETBRAINS_COMPOSE_DEV)
+RepositoryIR(DefaultRepository.JCENTER)
+RepositoryIR(DefaultRepository.GOOGLE)
}
override fun Reader.updateBuildFileIRs(irs: List<BuildSystemIR>): List<BuildSystemIR> = irs.filterNot {
it.safeAs<GradleOnlyPluginByNameIR>()?.pluginId == AndroidModuleConfigurator.DEPENDENCIES.KOTLIN_ANDROID_EXTENSIONS_NAME
}
override fun Reader.updateModuleIR(module: ModuleIR): ModuleIR {
val irs = module.irs.filterNot { ir ->
ir == AndroidSinglePlatformModuleConfigurator.DEPENDENCIES.APP_COMPAT
|| ir == AndroidSinglePlatformModuleConfigurator.DEPENDENCIES.CONSTRAINT_LAYOUT
|| ir == AndroidModuleConfigurator.DEPENDENCIES.MATERIAL
}
return module.withReplacedIrs(irs.toPersistentList())
}
override fun Writer.runArbitratyTask(module: ModuleIR): TaskResult<Unit> = compute {
BuildSystemPlugin.pluginRepositoreis.addValues(Repositories.JETBRAINS_COMPOSE_DEV).ensure()
//TODO hacky!
TemplatesPlugin.fileTemplatesToRender.update { templates ->
templates.mapNotNull { template ->
val descriptor = template.descriptor as? FileTemplateDescriptor
when {
descriptor == AndroidModuleConfigurator.FileTemplateDescriptors.activityMainXml
|| descriptor == AndroidModuleConfigurator.FileTemplateDescriptors.colorsXml
|| descriptor == AndroidModuleConfigurator.FileTemplateDescriptors.stylesXml -> null
descriptor?.templateId == "android/MainActivity.kt.vm" -> {
template.copy(descriptor = mainActivityKt(module.originalModule.javaPackage(pomIR())))
}
descriptor?.templateId == "android/AndroidManifest.xml.vm" -> {
template.copy(descriptor = Descriptors.manifestXml)
}
else -> template
}
}.asSuccess()
}
}
private fun mainActivityKt(javaPackage: JavaPackage) = FileTemplateDescriptor(
"composeAndroid/MainActivity.kt.vm",
"src" / "main" / "java" / javaPackage.asPath() / "MainActivity.kt"
)
object Descriptors {
val manifestXml = FileTemplateDescriptor(
templateId = "composeAndroid/AndroidManifest.xml.vm",
relativePath = "src" / "main" / "AndroidManifest.xml",
)
}
}
@@ -14,15 +14,16 @@ import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.GradleImpo
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.irsList
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.TargetConfigurationIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.addWithJavaIntoJvmTarget
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.AndroidSinglePlatformModuleConfigurator
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.inContextOfModuleConfigurator
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.moduleType
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemPlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.DefaultRepository
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Repositories
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
import org.jetbrains.kotlin.tools.projectWizard.templates.FileTemplateDescriptor
import org.jetbrains.kotlin.tools.projectWizard.templates.FileTemplateDescriptorWithPath
import org.jetbrains.kotlin.tools.projectWizard.templates.Template
import org.jetbrains.kotlin.tools.projectWizard.templates.asSrcOf
import org.jetbrains.kotlin.tools.projectWizard.plugins.pomIR
import org.jetbrains.kotlin.tools.projectWizard.settings.JavaPackage
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.settings.javaPackage
import org.jetbrains.kotlin.tools.projectWizard.templates.*
class ComposeJvmDesktopTemplate : Template() {
@NonNls
@@ -31,7 +32,11 @@ class ComposeJvmDesktopTemplate : Template() {
override val title: String = KotlinNewProjectWizardBundle.message("module.template.compose.desktop.title")
override val description: String = KotlinNewProjectWizardBundle.message("module.template.compose.desktop.description")
override val moduleTypes: Set<ModuleType> = setOf(ModuleType.jvm)
override fun isSupportedByModuleType(module: Module): Boolean =
module.configurator.moduleType == ModuleType.jvm
override fun isApplicableTo(reader: Reader, module: Module): Boolean =
module.kind == ModuleKind.singleplatformJvm
override fun Writer.getIrsToAddToBuildFile(
module: ModuleIR
@@ -45,17 +50,23 @@ class ComposeJvmDesktopTemplate : Template() {
}
override fun Writer.getRequiredLibraries(module: ModuleIR): List<DependencyIR> = listOf(
CustomGradleDependencyDependencyIR("compose.desktop.all", dependencyType = DependencyType.MAIN)
CustomGradleDependencyDependencyIR("compose.desktop.all", dependencyType = DependencyType.MAIN, DependencyKind.implementation)
)
override fun Writer.runArbitratyTask(module: ModuleIR): TaskResult<Unit> =
BuildSystemPlugin.pluginRepositoreis.addValues(Repositories.JETBRAINS_COMPOSE_DEV)
override fun updateTargetIr(module: ModuleIR, targetConfigurationIR: TargetConfigurationIR): TargetConfigurationIR =
targetConfigurationIR.addWithJavaIntoJvmTarget()
override fun Reader.getFileTemplates(module: ModuleIR) =
buildList<FileTemplateDescriptorWithPath> {
override fun Reader.getFileTemplates(module: ModuleIR) = buildList<FileTemplateDescriptorWithPath> {
val dependsOnMppModule: Module? =
module.originalModule.dependencies.map { moduleByReference(it) }.firstOrNull { it.template is ComposeMppModuleTemplate }
if (dependsOnMppModule == null) {
+(FileTemplateDescriptor("$id/main.kt", "main.kt".asPath()) asSrcOf SourcesetType.main)
} else {
val javaPackage = dependsOnMppModule.javaPackage(pomIR()).asCodePackage()
+(FileTemplateDescriptor("composeMpp/main.kt.vm", "main.kt".asPath())
asSrcOf SourcesetType.main
withSettings ("sharedPackage" to javaPackage)
)
}
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.tools.projectWizard.templates.compose
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.tools.projectWizard.KotlinNewProjectWizardBundle
import org.jetbrains.kotlin.tools.projectWizard.Versions
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.core.safeAs
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.BuildScriptDependencyIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.BuildScriptRepositoryIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.GradleImportIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.irsList
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.TargetConfigurationIR
import org.jetbrains.kotlin.tools.projectWizard.library.MavenArtifact
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.*
import org.jetbrains.kotlin.tools.projectWizard.mpp.applyMppStructure
import org.jetbrains.kotlin.tools.projectWizard.mpp.mppSources
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
import org.jetbrains.kotlin.tools.projectWizard.plugins.pomIR
import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.DefaultRepository
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Repositories
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
import org.jetbrains.kotlin.tools.projectWizard.settings.javaPackage
import org.jetbrains.kotlin.tools.projectWizard.templates.FileTemplateDescriptor
import org.jetbrains.kotlin.tools.projectWizard.templates.Template
class ComposeMppModuleTemplate : Template() {
@NonNls
override val id: String = "composeMppModule"
override val title: String = KotlinNewProjectWizardBundle.message("module.template.compose.mpp.title")
override val description: String = KotlinNewProjectWizardBundle.message("module.template.compose.mpp.description")
override fun isSupportedByModuleType(module: Module): Boolean =
module.configurator == MppModuleConfigurator
override fun Writer.getIrsToAddToBuildFile(module: ModuleIR): List<BuildSystemIR> = irsList {
+GradleImportIR("org.jetbrains.compose.compose")
+GradleOnlyPluginByNameIR("org.jetbrains.compose", version = Versions.JETBRAINS_COMPOSE)
+RepositoryIR(Repositories.JETBRAINS_COMPOSE_DEV)
+RepositoryIR(DefaultRepository.JCENTER)
+RepositoryIR(DefaultRepository.GOOGLE)
}
override fun Reader.updateBuildFileIRs(irs: List<BuildSystemIR>): List<BuildSystemIR> = irs.filterNot {
it.safeAs<GradleOnlyPluginByNameIR>()?.pluginId == AndroidModuleConfigurator.DEPENDENCIES.KOTLIN_ANDROID_EXTENSIONS_NAME
}
override fun Reader.updateModuleIR(module: ModuleIR): ModuleIR = when (module.originalModule.configurator) {
CommonTargetConfigurator -> module.withIrs(
CustomGradleDependencyDependencyIR("compose.runtime", DependencyType.MAIN, DependencyKind.api),
CustomGradleDependencyDependencyIR("compose.foundation", DependencyType.MAIN, DependencyKind.api),
CustomGradleDependencyDependencyIR("compose.material", DependencyType.MAIN, DependencyKind.api),
)
AndroidTargetConfigurator -> module.withIrs(
AndroidSinglePlatformModuleConfigurator.DEPENDENCIES.APP_COMPAT.withDependencyKind(DependencyKind.api),
DEPENDENCIES.ANDROID_KTX.withDependencyKind(DependencyKind.api)
).withoutIrs { it == AndroidModuleConfigurator.DEPENDENCIES.MATERIAL }
else -> module
}
override fun Writer.runArbitratyTask(module: ModuleIR): TaskResult<Unit> = inContextOfModuleConfigurator(module.originalModule) {
val javaPackage = module.originalModule.javaPackage(pomIR())
val mpp = mppSources(javaPackage) {
mppFile("platform.kt") {
function("getPlatformName(): String") {
actualFor(ModuleSubType.jvm, actualBody = """return "Desktop" """)
actualFor(ModuleSubType.android, actualBody = """return "Android" """)
default("""return "Platform" """)
}
}
filesFor(ModuleSubType.common) {
file(FileTemplateDescriptor("composeMpp/App.kt.vm", relativePath = null), "App.kt", SourcesetType.main)
}
}
applyMppStructure(mpp, module.originalModule, module.path)
}
object DEPENDENCIES {
val ANDROID_KTX = ArtifactBasedLibraryDependencyIR(
MavenArtifact(DefaultRepository.GOOGLE, "androidx.core", "core-ktx"),
version = Versions.ANDROID.ANDROIDX_KTX,
dependencyType = DependencyType.MAIN
)
}
}