Wizard: add test infrastructure, project importing tests & project templates tests

This commit is contained in:
Ilya Kirillov
2020-03-06 19:15:57 +03:00
parent 2f30195a6d
commit 42529ec525
117 changed files with 2118 additions and 231 deletions
@@ -2,46 +2,57 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard
import org.jetbrains.kotlin.tools.projectWizard.YamlSettingsParser
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.core.context.ReadingContext
import org.jetbrains.kotlin.tools.projectWizard.core.context.SettingsWritingContext
import org.jetbrains.kotlin.tools.projectWizard.core.entity.PipelineTask
import org.jetbrains.kotlin.tools.projectWizard.core.entity.PluginSettingReference
import org.jetbrains.kotlin.tools.projectWizard.core.entity.PluginSetting
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
import org.jetbrains.kotlin.tools.projectWizard.core.service.*
import org.jetbrains.kotlin.tools.projectWizard.core.service.IdeaIndependentWizardService
import org.jetbrains.kotlin.tools.projectWizard.core.service.Services
import org.jetbrains.kotlin.tools.projectWizard.core.service.ServicesManager
import org.jetbrains.kotlin.tools.projectWizard.core.service.WizardService
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.plugins.StructurePlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin
import java.nio.file.Paths
import java.nio.file.Path
class YamlWizard(
private val yaml: String,
private val path: String,
private val projectPath: Path,
createPlugins: (Context) -> List<Plugin>,
servicesManager: ServicesManager = CLI_SERVICES_MANAGER,
isUnitTestMode: Boolean
) : Wizard(
createPlugins,
ServicesManager(Services.IDEA_INDEPENDENT_SERVICES) { services ->
services.firstOrNull { it is IdeaIndependentWizardService }
}, isUnitTestMode
) {
) : Wizard(createPlugins, servicesManager, isUnitTestMode) {
private val settingsWritingContext = SettingsWritingContext(context, servicesManager, isUnitTestMode)
override fun apply(
services: List<WizardService>,
phases: Set<GenerationPhase>,
onTaskExecuting: (PipelineTask) -> Unit
): TaskResult<Unit> = computeM {
super.apply(services, setOf(GenerationPhase.PREPARE), onTaskExecuting)
val parsingData = with(valuesReadingContext) {
ParsingState(TemplatesPlugin::templates.propertyValue, emptyMap())
val (settingsValuesFromYaml) = valuesReadingContext.parseYaml(yaml, pluginSettings)
with(settingsWritingContext) {
settingsValuesFromYaml.forEach { (reference, value) -> reference.setValue(value) }
StructurePlugin::projectPath.reference.setValue(projectPath)
}
val yamlParser = YamlSettingsParser(pluginSettings, parsingData)
val (settingsValuesFromYaml) = yamlParser.parseYamlText(yaml)
val settingValues = defaultPluginSettingValues + settingsValuesFromYaml
settingValues.forEach { (path, value) -> context.settingContext[path] = value }
context.settingContext[StructurePlugin::projectPath.reference] = Paths.get(path)
super.apply(services, phases, onTaskExecuting)
}
private val defaultPluginSettingValues
get() = pluginSettings.mapNotNull { setting ->
val defaultValue = setting.defaultValue ?: return@mapNotNull null
PluginSettingReference(setting) to defaultValue
}.toMap()
companion object {
val CLI_SERVICES_MANAGER = ServicesManager(Services.IDEA_INDEPENDENT_SERVICES) { services ->
services.firstOrNull { it is IdeaIndependentWizardService }
}
}
}
fun ReadingContext.parseYaml(
yaml: String,
pluginSettings: List<PluginSetting<*, *>>
): TaskResult<Map<SettingReference<*, *>, Any>> {
val parsingData = ParsingState(TemplatesPlugin::templates.propertyValue, emptyMap())
val yamlParser = YamlSettingsParser(pluginSettings, parsingData)
return yamlParser.parseYamlText(yaml)
}
@@ -0,0 +1,18 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
jcenter()
}
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == 'com.android' || requested.id.name == 'kotlin-android-extensions') {
useModule('com.android.tools.build:gradle:3.5.2')
}
}
}
}
rootProject.name = 'generatedProject'
include(':android')
@@ -0,0 +1,18 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
jcenter()
}
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == "com.android" || requested.id.name == "kotlin-android-extensions") {
useModule("com.android.tools.build:gradle:3.5.2")
}
}
}
}
rootProject.name = "generatedProject"
include(":android")
@@ -6,8 +6,4 @@ kotlin:
- type:
name: android
androidSdkPath: /home/user/Android/Sdk
kind: singleplatformJvm
name: android
sourcesets:
- type: main
- type: test
name: android
@@ -0,0 +1 @@
rootProject.name = 'generatedProject'
@@ -0,0 +1 @@
rootProject.name = "generatedProject"
@@ -2,20 +2,11 @@ kotlin:
projectKind: Multiplatform
modules:
- type: multiplatform
kind: multiplatform
name: a
subModules:
- type: jsNode
kind: target
name: nodeJs
sourcesets:
- type: main
- type: test
- type:
name: jsBrowser
testFramework: JS
kind: target
name: browser
sourcesets:
- type: main
- type: test
name: browser
@@ -0,0 +1 @@
rootProject.name = 'generatedProject'
@@ -0,0 +1 @@
rootProject.name = "generatedProject"
@@ -2,15 +2,10 @@ kotlin:
projectKind: Multiplatform
modules:
- type: multiplatform
kind: multiplatform
name: a
subModules:
- type:
targetJvmVersion: "1.8"
name: jvmTarget
testFramework: JUNIT4
kind: target
name: jvm
sourcesets:
- type: main
- type: test
name: jvm
@@ -0,0 +1 @@
rootProject.name = 'generatedProject'
@@ -0,0 +1 @@
rootProject.name = "generatedProject"
@@ -5,8 +5,4 @@ kotlin:
targetJvmVersion: "1.6"
name: JVM Module
testFramework: JUNIT4
name: nya
kind: singleplatformJvm
sourcesets:
- type: main
name: nya
@@ -0,0 +1 @@
rootProject.name = 'generatedProject'
@@ -0,0 +1 @@
rootProject.name = "generatedProject"
@@ -2,12 +2,7 @@ kotlin:
projectKind: Multiplatform
modules:
- type: multiplatform
kind: multiplatform
name: a
subModules:
- type: nativeForCurrentSystem
kind: target
name: myNative
sourcesets:
- type: main
- type: test
name: myNative
@@ -0,0 +1 @@
rootProject.name = 'generatedProject'
@@ -0,0 +1 @@
rootProject.name = "generatedProject"
@@ -2,23 +2,14 @@ kotlin:
projectKind: Multiplatform
modules:
- type: multiplatform
kind: multiplatform
name: a
subModules:
- type:
targetJvmVersion: "9"
name: jvmTarget
testFramework: JUNIT4
kind: target
name: jvm
sourcesets:
- type: main
- type: test
- type:
name: jsBrowser
testFramework: JS
kind: target
name: a
sourcesets:
- type: main
- type: test
name: a
@@ -0,0 +1 @@
rootProject.name = 'generatedProject'
@@ -0,0 +1 @@
rootProject.name = "generatedProject"
@@ -2,12 +2,7 @@ kotlin:
projectKind: Multiplatform
modules:
- type: multiplatform
kind: multiplatform
name: a
subModules:
- type: linuxX64Target
kind: target
name: linuxX64
sourcesets:
- type: main
- type: test
name: linuxX64
@@ -0,0 +1,14 @@
plugins {
id 'org.jetbrains.kotlin.js' version '1.3.61'
}
group = 'testGroupId'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test-js'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-js'
}
kotlin.target.browser { }
@@ -0,0 +1,14 @@
plugins {
kotlin("js") version "1.3.61"
}
group = "testGroupId"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
testImplementation(kotlin("test-js"))
implementation(kotlin("stdlib-js"))
}
kotlin.target.browser { }
@@ -0,0 +1 @@
rootProject.name = 'generatedProject'
@@ -0,0 +1,5 @@
kotlin:
projectKind: js
modules:
- type: jsSinglepaltform
name: a
@@ -0,0 +1,35 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android' version '1.3.61'
id 'kotlin-android-extensions'
}
group = 'me.user'
version = '1.0-SNAPSHOT'
repositories {
gradlePluginPortal()
google()
jcenter()
mavenCentral()
}
dependencies {
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7'
}
android {
compileSdkVersion 29
defaultConfig {
applicationId 'me.user.app'
minSdkVersion 24
targetSdkVersion 29
versionCode 1
versionName '1.0'
}
buildTypes {
'release' {
minifyEnabled false
}
}
}
@@ -0,0 +1,35 @@
plugins {
id("com.android.application")
kotlin("android") version "1.3.61"
id("kotlin-android-extensions")
}
group = "me.user"
version = "1.0-SNAPSHOT"
repositories {
gradlePluginPortal()
google()
jcenter()
mavenCentral()
}
dependencies {
implementation("androidx.core:core-ktx:1.1.0")
implementation("androidx.appcompat:appcompat:1.1.0")
implementation("androidx.constraintlayout:constraintlayout:1.1.3")
implementation(kotlin("stdlib-jdk7"))
}
android {
compileSdkVersion(29)
defaultConfig {
applicationId = "me.user.app"
minSdkVersion(24)
targetSdkVersion(29)
versionCode = 1
versionName = "1.0"
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
}
@@ -0,0 +1,6 @@
group = 'me.user'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
@@ -0,0 +1,6 @@
group = "me.user"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
@@ -0,0 +1,18 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
jcenter()
}
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == 'com.android' || requested.id.name == 'kotlin-android-extensions') {
useModule('com.android.tools.build:gradle:3.5.2')
}
}
}
}
rootProject.name = 'AndroidApplication'
include(':app')
@@ -0,0 +1,18 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
jcenter()
}
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == "com.android" || requested.id.name == "kotlin-android-extensions") {
useModule("com.android.tools.build:gradle:3.5.2")
}
}
}
}
rootProject.name = "AndroidApplication"
include(":app")
@@ -0,0 +1,25 @@
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
}
group = 'me.user'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
kotlin {
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib-common')
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
}
}
@@ -0,0 +1,25 @@
plugins {
kotlin("multiplatform") version "1.3.61"
}
group = "me.user"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
}
}
@@ -0,0 +1,18 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
}
group = 'me.user'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
}
tasks.withType(KotlinCompile) {
kotlinOptions.jvmTarget = '1.8'
}
@@ -0,0 +1,18 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.3.61"
}
group = "me.user"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
testImplementation(kotlin("test-junit"))
implementation(kotlin("stdlib-jdk8"))
}
tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "1.8"
}
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>mainModule</artifactId>
<groupId>me.user</groupId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mainModule</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
</properties>
<repositories>
<repository>
<id>mavenCentral</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>1.3.61</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>1.3.61</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.3.61</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,27 @@
plugins {
id 'org.jetbrains.kotlin.js' version '1.3.61'
}
group = 'me.user'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
jcenter()
maven {
url 'https://dl.bintray.com/kotlin/kotlin-js-wrappers'
}
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test-js'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-js'
implementation 'org.jetbrains.kotlinx:kotlinx-html-js:0.6.12'
implementation 'org.jetbrains:kotlin-react:16.9.0-pre.89-kotlin-1.3.60'
implementation 'org.jetbrains:kotlin-react-dom:16.9.0-pre.89-kotlin-1.3.60'
implementation npm('react','16.12.0')
implementation npm('react-dom','16.12.0')
implementation npm('react-is','16.12.0')
implementation 'org.jetbrains:kotlin-styled:1.0.0-pre.89-kotlin-1.3.60'
implementation npm('styled-components','5.0.0')
implementation npm('inline-style-prefixer','5.1.0')
}
kotlin.target.browser { }
@@ -0,0 +1,27 @@
plugins {
kotlin("js") version "1.3.61"
}
group = "me.user"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
jcenter()
maven {
url = uri("https://dl.bintray.com/kotlin/kotlin-js-wrappers")
}
}
dependencies {
testImplementation(kotlin("test-js"))
implementation(kotlin("stdlib-js"))
implementation("org.jetbrains.kotlinx:kotlinx-html-js:0.6.12")
implementation("org.jetbrains:kotlin-react:16.9.0-pre.89-kotlin-1.3.60")
implementation("org.jetbrains:kotlin-react-dom:16.9.0-pre.89-kotlin-1.3.60")
implementation(npm("react","16.12.0"))
implementation(npm("react-dom","16.12.0"))
implementation(npm("react-is","16.12.0"))
implementation("org.jetbrains:kotlin-styled:1.0.0-pre.89-kotlin-1.3.60")
implementation(npm("styled-components","5.0.0"))
implementation(npm("inline-style-prefixer","5.1.0"))
}
kotlin.target.browser { }
@@ -0,0 +1,22 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
id 'application'
}
group = 'me.user'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
}
tasks.withType(KotlinCompile) {
kotlinOptions.jvmTarget = '1.8'
}
application {
mainClassName = 'MainKt'
}
@@ -0,0 +1,22 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.3.61"
application
}
group = "me.user"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
testImplementation(kotlin("test-junit"))
implementation(kotlin("stdlib-jdk8"))
}
tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "1.8"
}
application {
mainClassName = "MainKt"
}
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>consoleApp</artifactId>
<groupId>me.user</groupId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>consoleApp</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
</properties>
<repositories>
<repository>
<id>mavenCentral</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>1.3.61</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>MainKt</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>1.3.61</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.3.61</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,81 @@
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
id 'application'
}
group = 'me.user'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
jcenter()
maven {
url 'https://dl.bintray.com/kotlin/ktor'
}
maven {
url 'https://dl.bintray.com/kotlin/kotlin-js-wrappers'
}
}
kotlin {
jvm {
compilations.all {
kotlinOptions.jvmTarget = '1.8'
}
withJava()
}
js {
browser {
}
}
sourceSets {
jvmMain {
dependencies {
implementation kotlin('stdlib-jdk8')
implementation 'io.ktor:ktor-server-netty:1.2.6'
implementation 'io.ktor:ktor-html-builder:1.2.6'
implementation 'org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.12'
}
}
jvmTest {
dependencies {
implementation kotlin('test-junit')
}
}
jsMain {
dependencies {
implementation kotlin('stdlib-js')
implementation 'org.jetbrains.kotlinx:kotlinx-html-js:0.6.12'
implementation 'org.jetbrains:kotlin-react:16.9.0-pre.89-kotlin-1.3.60'
implementation 'org.jetbrains:kotlin-react-dom:16.9.0-pre.89-kotlin-1.3.60'
implementation npm('react','16.12.0')
implementation npm('react-dom','16.12.0')
implementation npm('react-is','16.12.0')
implementation 'org.jetbrains:kotlin-styled:1.0.0-pre.89-kotlin-1.3.60'
implementation npm('styled-components','5.0.0')
implementation npm('inline-style-prefixer','5.1.0')
}
}
jsTest {
dependencies {
implementation kotlin('test-js')
}
}
}
}
application {
mainClassName = 'ServerKt'
}
tasks.getByName('jsBrowserProductionWebpack') {
outputFileName = 'output.js'
}
tasks.getByName('jvmJar') {
dependsOn(tasks.getByName('jsBrowserProductionWebpack'))
def jsBrowserProductionWebpack = tasks.getByName('jsBrowserProductionWebpack')
from(new File(jsBrowserProductionWebpack.destinationDirectory, jsBrowserProductionWebpack.outputFileName))
}
tasks.getByName('run') {
dependsOn(tasks.getByName('jvmJar'))
classpath(tasks.getByName('jvmJar'))
}
@@ -0,0 +1,81 @@
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
plugins {
kotlin("multiplatform") version "1.3.61"
application
}
group = "me.user"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
jcenter()
maven {
url = uri("https://dl.bintray.com/kotlin/ktor")
}
maven {
url = uri("https://dl.bintray.com/kotlin/kotlin-js-wrappers")
}
}
kotlin {
jvm {
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
withJava()
}
js {
browser {
}
}
sourceSets {
val jvmMain by getting {
dependencies {
implementation(kotlin("stdlib-jdk8"))
implementation("io.ktor:ktor-server-netty:1.2.6")
implementation("io.ktor:ktor-html-builder:1.2.6")
implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.12")
}
}
val jvmTest by getting {
dependencies {
implementation(kotlin("test-junit"))
}
}
val jsMain by getting {
dependencies {
implementation(kotlin("stdlib-js"))
implementation("org.jetbrains.kotlinx:kotlinx-html-js:0.6.12")
implementation("org.jetbrains:kotlin-react:16.9.0-pre.89-kotlin-1.3.60")
implementation("org.jetbrains:kotlin-react-dom:16.9.0-pre.89-kotlin-1.3.60")
implementation(npm("react","16.12.0"))
implementation(npm("react-dom","16.12.0"))
implementation(npm("react-is","16.12.0"))
implementation("org.jetbrains:kotlin-styled:1.0.0-pre.89-kotlin-1.3.60")
implementation(npm("styled-components","5.0.0"))
implementation(npm("inline-style-prefixer","5.1.0"))
}
}
val jsTest by getting {
dependencies {
implementation(kotlin("test-js"))
}
}
}
}
application {
mainClassName = "ServerKt"
}
tasks.getByName<KotlinWebpack>("jsBrowserProductionWebpack") {
outputFileName = "output.js"
}
tasks.getByName<Jar>("jvmJar") {
dependsOn(tasks.getByName("jsBrowserProductionWebpack"))
val jsBrowserProductionWebpack = tasks.getByName<KotlinWebpack>("jsBrowserProductionWebpack")
from(File(jsBrowserProductionWebpack.destinationDirectory, jsBrowserProductionWebpack.outputFileName))
}
tasks.getByName<JavaExec>("run") {
dependsOn(tasks.getByName<Jar>("jvmJar"))
classpath(tasks.getByName<Jar>("jvmJar"))
}
@@ -0,0 +1,71 @@
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTests
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
}
group = 'me.user'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
kotlin {
jvm {
compilations.all {
kotlinOptions.jvmTarget = '1.8'
}
}
js {
browser {
}
}
def hostOs = System.getProperty("os.name")
def isMingwX64 = hostOs.startsWith("Windows")
KotlinNativeTargetWithTests nativeTarget
if (hostOs == "Mac OS X") nativeTarget = macosX64('native')
else if (hostOs == "Linux") nativeTarget = linuxX64("native")
else if (isMingwX64) nativeTarget = mingwX64("native")
else throw new GradleException("Host OS is not supported in Kotlin/Native.")
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib-common')
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmMain {
dependencies {
implementation kotlin('stdlib-jdk8')
}
}
jvmTest {
dependencies {
implementation kotlin('test-junit')
}
}
jsMain {
dependencies {
implementation kotlin('stdlib-js')
}
}
jsTest {
dependencies {
implementation kotlin('test-js')
}
}
nativeMain {
}
nativeTest {
}
}
}
@@ -0,0 +1,66 @@
plugins {
kotlin("multiplatform") version "1.3.61"
}
group = "me.user"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
kotlin {
jvm {
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
}
js {
browser {
}
}
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val jvmMain by getting {
dependencies {
implementation(kotlin("stdlib-jdk8"))
}
}
val jvmTest by getting {
dependencies {
implementation(kotlin("test-junit"))
}
}
val jsMain by getting {
dependencies {
implementation(kotlin("stdlib-js"))
}
}
val jsTest by getting {
dependencies {
implementation(kotlin("test-js"))
}
}
val nativeMain by getting
val nativeTest by getting
}
}
@@ -0,0 +1,36 @@
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTests
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
}
group = 'me.user'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
kotlin {
def hostOs = System.getProperty("os.name")
def isMingwX64 = hostOs.startsWith("Windows")
KotlinNativeTargetWithTests nativeTarget
if (hostOs == "Mac OS X") nativeTarget = macosX64('native')
else if (hostOs == "Linux") nativeTarget = linuxX64("native")
else if (isMingwX64) nativeTarget = mingwX64("native")
else throw new GradleException("Host OS is not supported in Kotlin/Native.")
nativeTarget.with {
binaries {
executable {
entryPoint = 'main'
}
}
}
sourceSets {
nativeMain {
}
nativeTest {
}
}
}
@@ -0,0 +1,31 @@
plugins {
kotlin("multiplatform") version "1.3.61"
}
group = "me.user"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
kotlin {
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
nativeTarget.apply {
binaries {
executable {
entryPoint = "main"
}
}
}
sourceSets {
val nativeMain by getting
val nativeTest by getting
}
}
@@ -0,0 +1 @@
rootProject.name = 'NativeConsoleApplication'
@@ -6,21 +6,17 @@
package org.jetbrains.kotlin.tools.projectWizard.cli
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.tools.projectWizard.core.ExceptionError
import org.jetbrains.kotlin.tools.projectWizard.core.div
import org.jetbrains.kotlin.tools.projectWizard.core.onFailure
import org.jetbrains.kotlin.tools.projectWizard.core.service.Services
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.plugins.Plugins
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.MavenPlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
import org.jetbrains.kotlin.tools.projectWizard.wizard.YamlWizard
import org.jetbrains.kotlin.tools.projectWizard.wizard.Wizard
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
abstract class AbstractBuildFileGenerationTest : UsefulTestCase() {
abstract fun createWizard(directory: Path, buildSystem: BuildSystem, projectDirectory: Path): Wizard
fun doTest(directoryPath: String) {
val directory = Paths.get(directoryPath)
val expectedDirectory = expectedDirectory(directory)
@@ -33,67 +29,26 @@ abstract class AbstractBuildFileGenerationTest : UsefulTestCase() {
}
private fun doTest(directory: Path, buildSystem: BuildSystem) {
val yaml = directory.resolve("settings.yaml").toFile().readText() + "\n" +
defaultStructure + "\n" +
buildSystem.yaml
val tempDir = Files.createTempDirectory(null)
val wizard = YamlWizard(yaml, tempDir.toString(), Plugins.allPlugins, isUnitTestMode = true)
val tempDirectory = Files.createTempDirectory(null)
val wizard = createWizard(directory, buildSystem, tempDirectory)
val result = wizard.apply(Services.IDEA_INDEPENDENT_SERVICES, GenerationPhase.ALL)
result.onFailure { errors ->
errors.forEach { error ->
if (error is ExceptionError) {
throw error.exception
}
}
fail(errors.joinToString("\n"))
}
result.assertSuccess()
val expectedDirectory = expectedDirectory(directory)
compareFiles(
expectedDirectory.allBuildFiles(buildSystem), expectedDirectory,
tempDir.allBuildFiles(buildSystem), tempDir
tempDirectory.allBuildFiles(buildSystem), tempDirectory
)
}
private fun Path.allBuildFiles(buildSystem: BuildSystem) =
listFiles { it.fileName.toString() == buildSystem.buildFileName }
private enum class BuildSystem(val buildFileName: String, val yaml: String) {
GRADLE_KOTLIN_DSL(
buildFileName = "build.gradle.kts",
yaml = """buildSystem:
type: GradleKotlinDsl
gradle:
createGradleWrapper: false
version: 5.4.1""".trimIndent()
),
GRADLE_GROOVY_DSL(
buildFileName = "build.gradle",
yaml = """buildSystem:
type: GradleGroovyDsl
gradle:
createGradleWrapper: false
version: 5.4.1""".trimIndent()
),
MAVEN(
buildFileName = "pom.xml",
yaml = """buildSystem:
type: Maven""".trimIndent()
)
}
listFiles { it.fileName.toString() in buildSystem.allBuildFileNames }
private fun expectedDirectory(directory: Path): Path =
(directory / EXPECTED_DIRECTORY_NAME).takeIf { Files.exists(it) } ?: directory
companion object {
private const val EXPECTED_DIRECTORY_NAME = "expected"
private val defaultStructure =
"""structure:
name: generatedProject
groupId: testGroupId
artifactId: testArtifactId
""".trimIndent()
}
}
@@ -0,0 +1,16 @@
/*
* 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.cli
import org.jetbrains.kotlin.tools.projectWizard.wizard.Wizard
import org.jetbrains.kotlin.tools.projectWizard.wizard.YamlWizard
import java.nio.file.Path
abstract class AbstractProjectTemplateBuildFileGenerationTest : AbstractBuildFileGenerationTest() {
override fun createWizard(directory: Path, buildSystem: BuildSystem, projectDirectory: Path): Wizard =
ProjectTemplateBasedTestWizard.createByDirectory(directory, buildSystem, projectDirectory, YamlWizard.CLI_SERVICES_MANAGER)
}
@@ -0,0 +1,18 @@
/*
* 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.cli
import org.jetbrains.kotlin.tools.projectWizard.plugins.Plugins
import org.jetbrains.kotlin.tools.projectWizard.wizard.Wizard
import org.jetbrains.kotlin.tools.projectWizard.wizard.YamlWizard
import java.nio.file.Path
abstract class AbstractYamlBuildFileGenerationTest : AbstractBuildFileGenerationTest() {
override fun createWizard(directory: Path, buildSystem: BuildSystem, projectDirectory: Path): Wizard {
val yaml = readSettingsYaml(directory, buildSystem) ?: error("settings.yaml should exists in $directory")
return YamlWizard(yaml, projectDirectory.toString(), Plugins.allPlugins, isUnitTestMode = true)
}
}
@@ -0,0 +1,84 @@
/*
* 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.cli
import org.jetbrains.kotlin.tools.projectWizard.core.TaskResult
import org.jetbrains.kotlin.tools.projectWizard.core.compute
import org.jetbrains.kotlin.tools.projectWizard.core.computeM
import org.jetbrains.kotlin.tools.projectWizard.core.context.SettingsWritingContext
import org.jetbrains.kotlin.tools.projectWizard.core.entity.PipelineTask
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
import org.jetbrains.kotlin.tools.projectWizard.core.service.ServicesManager
import org.jetbrains.kotlin.tools.projectWizard.core.service.WizardService
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.plugins.Plugins
import org.jetbrains.kotlin.tools.projectWizard.plugins.StructurePlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemPlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.projectTemplates.applyProjectTemplate
import org.jetbrains.kotlin.tools.projectWizard.projectTemplates.ProjectTemplate
import org.jetbrains.kotlin.tools.projectWizard.wizard.Wizard
import org.jetbrains.kotlin.tools.projectWizard.wizard.YamlWizard
import org.jetbrains.kotlin.tools.projectWizard.wizard.parseYaml
import java.nio.file.Path
class ProjectTemplateBasedTestWizard(
private val projectTemplate: ProjectTemplate,
private val buildSystem: BuildSystem,
private val projectDirectory: Path,
servicesManager: ServicesManager,
private val additionalYamlSettings: String?
) : Wizard(
Plugins.allPlugins,
servicesManager,
isUnitTestMode = true
) {
private val settingsWritingContext = SettingsWritingContext(context, servicesManager, isUnitTestMode = true)
override fun apply(
services: List<WizardService>,
phases: Set<GenerationPhase>,
onTaskExecuting: (PipelineTask) -> Unit
): TaskResult<Unit> =
computeM {
super.apply(services, setOf(GenerationPhase.PREPARE), onTaskExecuting).ensure()
with(settingsWritingContext) {
applyProjectTemplate(projectTemplate)
BuildSystemPlugin::type.reference.setValue(buildSystem.buildSystemType)
StructurePlugin::projectPath.reference.setValue(projectDirectory)
StructurePlugin::name.reference.setValue(projectTemplate.id)
StructurePlugin::groupId.reference.setValue(GROUP_ID)
StructurePlugin::artifactId.reference.setValue(ARTIFACT_ID)
applyAdditionalSettingsFromYaml().ensure()
}
super.apply(services, phases, onTaskExecuting)
}
private fun SettingsWritingContext.applyAdditionalSettingsFromYaml() = compute {
if (additionalYamlSettings != null) {
val (additionalSettings) = parseYaml(additionalYamlSettings, pluginSettings)
additionalSettings.forEach { (reference, value) -> reference.setValue(value) }
}
}
companion object {
private const val GROUP_ID = "me.user"
private const val ARTIFACT_ID = "artifactId"
fun createByDirectory(
directory: Path,
buildSystem: BuildSystem,
projectDirectory: Path,
servicesManager: ServicesManager
): ProjectTemplateBasedTestWizard {
val projectTemplateId = directory.fileName.toString()
val projectTemplate = ProjectTemplate.byId(projectTemplateId)
?: error("Directory name should be a valid template id but was $projectTemplateId")
val additionalYamlSettings = readSettingsYamlWithoutDefaultStructure(directory)
return ProjectTemplateBasedTestWizard(projectTemplate, buildSystem, projectDirectory, servicesManager, additionalYamlSettings)
}
}
}
@@ -0,0 +1,70 @@
/*
* 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.cli;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/projectTemplatesBuildFileGeneration")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class ProjectTemplateBuildFileGenerationTestGenerated extends AbstractProjectTemplateBuildFileGenerationTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInProjectTemplatesBuildFileGeneration() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/projectTemplatesBuildFileGeneration"), Pattern.compile("^([^\\.]+)$"), null, false);
}
@TestMetadata("androidApplication")
public void testAndroidApplication() throws Exception {
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/projectTemplatesBuildFileGeneration/androidApplication/");
}
@TestMetadata("emptyMultiplatform")
public void testEmptyMultiplatform() throws Exception {
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/projectTemplatesBuildFileGeneration/emptyMultiplatform/");
}
@TestMetadata("emptySingleplatform")
public void testEmptySingleplatform() throws Exception {
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/projectTemplatesBuildFileGeneration/emptySingleplatform/");
}
@TestMetadata("jsBrowserApplication")
public void testJsBrowserApplication() throws Exception {
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/projectTemplatesBuildFileGeneration/jsBrowserApplication/");
}
@TestMetadata("jvmConsoleApplication")
public void testJvmConsoleApplication() throws Exception {
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/projectTemplatesBuildFileGeneration/jvmConsoleApplication/");
}
@TestMetadata("jvmServerJsClient")
public void testJvmServerJsClient() throws Exception {
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/projectTemplatesBuildFileGeneration/jvmServerJsClient/");
}
@TestMetadata("multiplatformLibrary")
public void testMultiplatformLibrary() throws Exception {
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/projectTemplatesBuildFileGeneration/multiplatformLibrary/");
}
@TestMetadata("nativeConsoleApplication")
public void testNativeConsoleApplication() throws Exception {
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/projectTemplatesBuildFileGeneration/nativeConsoleApplication/");
}
}
@@ -0,0 +1,50 @@
/*
* 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.cli
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.nio.file.Files
import java.nio.file.Path
import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.full.primaryConstructor
interface TestParameters {
companion object {
inline fun <reified T : TestParameters> fromTestDataOrDefault(
directory: Path,
parametersFileName: String = "parameters.txt"
): T =
fromTestData(directory, parametersFileName, T::class) ?: T::class.DEFAULT_VALUE
val <T : TestParameters> KClass<T>.DEFAULT_VALUE
get() = primaryConstructorOrError.callBy(emptyMap())
fun <T : TestParameters> fromTestData(directory: Path, parametersFileName: String, klass: KClass<T>): T? {
val file = directory.resolve(parametersFileName)
if (!Files.exists(file) || !Files.isRegularFile(file)) return null
val text = file.readFile()
return parseTestParameters(text, klass)
}
private fun <T : TestParameters> parseTestParameters(text: String, klass: KClass<T>): T {
val constructor = klass.primaryConstructorOrError
val parameters = constructor.parameters.associateWith { parameter ->
parameter.parseValue(text)
}
return constructor.callBy(parameters)
}
private fun KParameter.parseValue(text: String): Any? = when (type.classifier as? KClass<*>) {
Boolean::class -> InTextDirectivesUtils.getPrefixedBoolean(text, name) ?: false
else -> error("Invalid test parameter $name with type $type")
}
private val <T : Any> KClass<T>.primaryConstructorOrError
get() = primaryConstructor ?: error("Primary constructor should present")
}
}
@@ -0,0 +1,11 @@
/*
* 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.cli
import org.jetbrains.kotlin.tools.projectWizard.core.service.WizardService
interface TestWizardService : WizardService {
}
@@ -19,7 +19,7 @@ import java.util.regex.Pattern;
@TestMetadata("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class BuildFileGenerationTestGenerated extends AbstractBuildFileGenerationTest {
public class YamlBuildFileGenerationTestGenerated extends AbstractYamlBuildFileGenerationTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -62,4 +62,9 @@ public class BuildFileGenerationTestGenerated extends AbstractBuildFileGeneratio
public void testSimpleNativeTarget() throws Exception {
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/simpleNativeTarget/");
}
@TestMetadata("singleplatformJs")
public void testSingleplatformJs() throws Exception {
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/singleplatformJs/");
}
}
@@ -5,8 +5,13 @@
package org.jetbrains.kotlin.tools.projectWizard.cli
import com.intellij.testFramework.UsefulTestCase
import org.hamcrest.core.Is
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.tools.projectWizard.core.ExceptionError
import org.jetbrains.kotlin.tools.projectWizard.core.TaskResult
import org.jetbrains.kotlin.tools.projectWizard.core.onFailure
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemType
import org.junit.Assert.assertThat
import java.nio.file.Files
import java.nio.file.Path
@@ -34,4 +39,74 @@ internal fun compareFiles(
for ((actualFile, expectedFile) in actualFilesSorted zip expectedFilesSorted) {
KotlinTestUtils.assertEqualsToFile(expectedFile.toFile(), actualFile.readFile())
}
}
fun readSettingsYaml(directory: Path, buildSystem: BuildSystem): String? =
readSettingsYamlWithoutDefaultStructure(directory)?.let { text ->
text + "\n" +
defaultStructure + "\n" +
buildSystem.yaml
}
fun readSettingsYamlWithoutDefaultStructure(directory: Path): String? =
directory.resolve("settings.yaml").takeIf { Files.exists(it) }?.readFile()
enum class BuildSystem(
val buildFileName: String,
val additionalFileNames: List<String> = emptyList(),
val yaml: String
) {
GRADLE_KOTLIN_DSL(
buildFileName = "build.gradle.kts",
additionalFileNames = listOf("settings.gradle.kts"),
yaml = """buildSystem:
type: GradleKotlinDsl
gradle:
version: 5.4.1""".trimIndent()
),
GRADLE_GROOVY_DSL(
buildFileName = "build.gradle",
additionalFileNames = listOf("settings.gradle"),
yaml = """buildSystem:
type: GradleGroovyDsl
gradle:
version: 5.4.1""".trimIndent()
),
MAVEN(
buildFileName = "pom.xml",
yaml = """buildSystem:
type: Maven""".trimIndent()
)
}
val BuildSystem.buildSystemType
get() = when (this) {
BuildSystem.GRADLE_KOTLIN_DSL -> BuildSystemType.GradleKotlinDsl
BuildSystem.GRADLE_GROOVY_DSL -> BuildSystemType.GradleGroovyDsl
BuildSystem.MAVEN -> BuildSystemType.Maven
}
val BuildSystem.allBuildFileNames
get() = additionalFileNames + buildFileName
val BuildSystem.isGradle
get() = this == BuildSystem.GRADLE_KOTLIN_DSL || this == BuildSystem.GRADLE_GROOVY_DSL
private val defaultStructure =
"""structure:
name: generatedProject
groupId: testGroupId
artifactId: testArtifactId
""".trimIndent()
fun TaskResult<Any>.assertSuccess() {
onFailure { errors ->
errors.forEach { error ->
if (error is ExceptionError) {
throw error.exception
}
}
UsefulTestCase.fail(errors.joinToString("\n"))
}
}
@@ -84,6 +84,6 @@ inline fun <reified E> SettingsOwner.enumSettingImpl(
crossinline init: DropDownSettingType.Builder<E>.() -> Unit = {}
) where E : Enum<E>, E : DisplayableSettingItem = dropDownSetting<E>(title, neededAtPhase, enumParser()) {
values = enumValues<E>().asList()
defaultValue = values.firstOrNull()
//
init()
}
@@ -39,14 +39,13 @@ fun <T : Any> Parser<T>.parse(context: ParsingContext, value: Any?, path: String
inline fun <reified E> enumParser(): Parser<E> where E : Enum<E>, E : DisplayableSettingItem = object : Parser<E>() {
override fun ParsingContext.parse(value: Any?, path: String): TaskResult<E> = computeM {
val (name) = value.parseAs<String>(path)
val createError = {
enumValues<E>().firstOrNull { enumValue ->
enumValue.name.equals(name, ignoreCase = true) || enumValue.text.equals(name, ignoreCase = true)
}.toResult {
ParseError(
"For setting `$path` one of [${enumValues<E>().joinToString { it.name }}] was expected but `$name` was found"
)
}
val byEnumName = safe { enumValueOf<E>(name) }.mapFailure { createError() }
val byText = enumValues<E>().firstOrNull { it.text == name }.toResult { createError() }
byEnumName.recover { byText }
}
}
@@ -57,13 +57,16 @@ open class ReadingContext(
return serializer.fromString(savedValue)
}
val <V : Any> Setting<V, SettingType<V>>.savedOrDefaultValue: V?
get() = getSavedValueForSetting() ?: defaultValue
val <V : Any> SettingReference<V, SettingType<V>>.savedOrDefaultValue: V?
get() = setting.getSavedValueForSetting() ?: when (val defaultValue = setting.defaultValue) {
is SettingDefaultValue.Value -> defaultValue.value
is SettingDefaultValue.Dynamic<V> -> defaultValue.getter(this@ReadingContext, this)
null -> null
}
val <V : Any, T : SettingType<V>> SettingReference<V, T>.setting: Setting<V, T>
get() = with(this) { getSetting() }
@PublishedApi
internal val `access$context`: Context
get() = context
@@ -133,7 +133,7 @@ class SettingContext(val onUpdated: (SettingReference<*, *>) -> Unit) {
fun ReadingContext.initPluginSettings(settings: List<PluginSetting<*, *>>) {
for (setting in settings) {
setting.savedOrDefaultValue?.let { values[setting.path] = it }
setting.reference.savedOrDefaultValue?.let { values[setting.path] = it }
}
}
@@ -166,7 +166,7 @@ typealias AnySetting = Setting<*, *>
interface Setting<out V : Any, out T : SettingType<V>> : Entity, ActivityCheckerOwner, Validatable<V> {
val title: String
val defaultValue: V?
val defaultValue: SettingDefaultValue<V>?
val isRequired: Boolean
val isSavable: Boolean
var neededAtPhase: GenerationPhase
@@ -176,7 +176,7 @@ interface Setting<out V : Any, out T : SettingType<V>> : Entity, ActivityChecker
data class InternalSetting<out V : Any, out T : SettingType<V>>(
override val path: String,
override val title: String,
override val defaultValue: V?,
override val defaultValue: SettingDefaultValue<V>?,
override val isAvailable: Checker,
override val isRequired: Boolean,
override val isSavable: Boolean,
@@ -200,6 +200,13 @@ class TemplateSetting<out V : Any, out T : SettingType<V>>(
) : SettingImpl<V, T>(), Setting<V, T> by internal
sealed class SettingDefaultValue<out V : Any> {
data class Value<V : Any>(val value: V) : SettingDefaultValue<V>()
data class Dynamic<V : Any>(
val getter: ReadingContext.(SettingReference<V, SettingType<V>>) -> V
) : SettingDefaultValue<V>()
}
abstract class SettingBuilder<V : Any, T : SettingType<V>>(
private val path: String,
@@ -207,10 +214,13 @@ abstract class SettingBuilder<V : Any, T : SettingType<V>>(
private val neededAtPhase: GenerationPhase
) {
var isAvailable: ReadingContext.() -> Boolean = { true }
var defaultValue: V? = null
open var defaultValue: SettingDefaultValue<V>? = null
var isSavable: Boolean = false
var isRequired: Boolean? = null
fun value(value: V) = SettingDefaultValue.Value(value)
fun dynamic(getter: ReadingContext.(SettingReference<V, SettingType<V>>) -> V) = SettingDefaultValue.Dynamic(getter)
protected var validator = SettingValidator<V> { ValidationResult.OK }
fun validate(validator: SettingValidator<V>) {
@@ -316,6 +326,16 @@ class DropDownSettingType<V : DisplayableSettingItem>(
override val type
get() = DropDownSettingType(values, filter, parser)
init {
defaultValue = dynamic { reference ->
values.first {
@Suppress("UNCHECKED_CAST")
filter(reference as SettingReference<V, DropDownSettingType<V>>, it)
}
}
}
}
}
@@ -71,6 +71,10 @@ data class ValidationError(val validationMessage: String) : Error() {
get() = validationMessage.capitalize()
}
data class ProjectImportingError(override val message: String) : Error()
data class InvalidSourceSetName(val name: String) : Error() {
override val message: String
get() = "Source set name `$name` is invalid"
@@ -7,10 +7,18 @@ import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemT
import java.nio.file.Path
interface ProjectImportingWizardService : BuildSystemWizardService {
fun importProject(path: Path, modulesIrs: List<ModuleIR>): TaskResult<Unit>
fun importProject(
path: Path,
modulesIrs: List<ModuleIR>,
buildSystem: BuildSystemType
): TaskResult<Unit>
}
class ProjectImportingWizardServiceImpl : ProjectImportingWizardService, IdeaIndependentWizardService {
override fun isSuitableFor(buildSystemType: BuildSystemType): Boolean = true
override fun importProject(path: Path, modulesIrs: List<ModuleIR>) = UNIT_SUCCESS
override fun importProject(
path: Path,
modulesIrs: List<ModuleIR>,
buildSystem: BuildSystemType
) = UNIT_SUCCESS
}
@@ -19,7 +19,7 @@ interface JSConfigurator : ModuleConfiguratorWithModuleType {
override val moduleType: ModuleType get() = ModuleType.js
}
object JsSingleplatformModuleConfigurator : JSConfigurator, ModuleConfiguratorWithTests {
object JsSingleplatformModuleConfigurator : JSConfigurator, ModuleConfiguratorWithTests, SinglePlatformModuleConfigurator {
override val moduleKind = ModuleKind.singleplatformJs
override val suggestedModuleName = "js"
override val id = "jsSinglepaltform"
@@ -169,8 +169,11 @@ interface ModuleConfiguratorWithSettings : ModuleConfigurator {
fun SettingsWritingContext.initDefaultValuesFor(module: Module) {
withSettingsOf(module) {
getConfiguratorSettings().forEach { setting ->
val defaultValue = setting.defaultValue ?: return@forEach
setting.reference.setValue(defaultValue)
val reference = setting.reference
val defaultValue = reference.savedOrDefaultValue ?: return@forEach
if (reference.notRequiredSettingValue == null) {
reference.setValue(defaultValue)
}
}
}
}
@@ -247,6 +250,7 @@ interface ModuleConfigurator : DisplayableSettingItem, EntitiesOwnerDescriptor {
+JvmSinglePlatformModuleConfigurator
+AndroidSinglePlatformModuleConfigurator
+IOSSinglePlatformModuleConfigurator
+JsSingleplatformModuleConfigurator
}
init {
@@ -6,8 +6,7 @@
package org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators
import org.jetbrains.kotlin.tools.projectWizard.core.context.ReadingContext
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ModuleConfiguratorSetting
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ModuleConfiguratorSettingReference
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
import org.jetbrains.kotlin.tools.projectWizard.core.safeAs
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.BuildSystemIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.DependencyType
@@ -26,8 +25,6 @@ interface ModuleConfiguratorWithTests : ModuleConfiguratorWithSettings {
"Test Framework",
neededAtPhase = GenerationPhase.PROJECT_GENERATION
) {
defaultValue = KotlinTestFramework.COMMON
filter = filter@{ reference, kotlinTestFramework ->
if (reference !is ModuleConfiguratorSettingReference<*, *>) return@filter true
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.isGradle
interface JvmModuleConfigurator : ModuleConfiguratorWithTests {
companion object : ModuleConfiguratorSettings() {
val targetJvmVersion by enumSetting<TargetJvmVersion>("Target JVM Version", GenerationPhase.PROJECT_GENERATION) {
defaultValue = TargetJvmVersion.JVM_1_8
defaultValue = value(TargetJvmVersion.JVM_1_8)
}
}
@@ -13,7 +13,7 @@ import java.nio.file.Paths
class StructurePlugin(context: Context) : Plugin(context) {
val projectPath by valueSetting("Root path", GenerationPhase.PROJECT_GENERATION, pathParser) {
defaultValue = Paths.get(".")
defaultValue = value(Paths.get("."))
}
val name by stringSetting("Name", GenerationPhase.PROJECT_GENERATION)
@@ -29,7 +29,7 @@ class StructurePlugin(context: Context) : Plugin(context) {
val version by stringSetting("Version", GenerationPhase.PROJECT_GENERATION) {
shouldNotBeBlank()
validate(StringValidators.shouldBeValidIdentifier("Version", setOf('_', '-', '.')))
defaultValue = "1.0-SNAPSHOT"
defaultValue = value("1.0-SNAPSHOT")
}
val createProjectDir by pipelineTask(GenerationPhase.PROJECT_GENERATION) {
@@ -87,8 +87,8 @@ abstract class BuildSystemPlugin(context: Context) : Plugin(context) {
runAfter(BuildSystemPlugin::createModules)
withAction {
val data = BuildSystemPlugin::buildSystemData.propertyValue.first { it.type == buildSystemType }
service<ProjectImportingWizardService> { service -> service.isSuitableFor(data.type) }!!
.importProject(StructurePlugin::projectPath.reference.settingValue, allIRModules)
service<ProjectImportingWizardService> { service -> service.isSuitableFor(data.type) }
.importProject(StructurePlugin::projectPath.reference.settingValue, allIRModules, buildSystemType)
}
}
@@ -24,14 +24,8 @@ import org.jetbrains.kotlin.tools.projectWizard.templates.FileTemplateDescriptor
abstract class GradlePlugin(context: Context) : BuildSystemPlugin(context) {
val createGradleWrapper by booleanSetting("Create Gradle Wrapper", GenerationPhase.FIRST_STEP) {
defaultValue = true
isAvailable = isGradle
}
val version by versionSetting("Gradle Version", GenerationPhase.FIRST_STEP) {
defaultValue = defaultVersions.first()
defaultValue = value(defaultVersions.first())
isAvailable = isGradle
}
@@ -92,7 +86,6 @@ abstract class GradlePlugin(context: Context) : BuildSystemPlugin(context) {
runBefore(TemplatesPlugin::renderFileTemplates)
isAvailable = isGradle
withAction {
if (!GradlePlugin::createGradleWrapper.reference.settingValue) return@withAction UNIT_SUCCESS
TemplatesPlugin::addFileTemplate.execute(
FileTemplate(
FileTemplateDescriptor(
@@ -1,29 +1,30 @@
package org.jetbrains.kotlin.tools.projectWizard.plugins.projectTemplates
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
import org.jetbrains.kotlin.tools.projectWizard.core.context.SettingsWritingContext
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.projectTemplates.CustomMultiplatformProjectTemplate
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.withAllSubModules
import org.jetbrains.kotlin.tools.projectWizard.projectTemplates.ProjectTemplate
class ProjectTemplatesPlugin(context: Context) : Plugin(context) {
val template by dropDownSetting<ProjectTemplate>(
"Template",
GenerationPhase.INIT_TEMPLATE,
valueParser { value, path ->
CustomMultiplatformProjectTemplate
}) {
parser = valueParserM { _, _ ->
Failure(ParseError("Project templates is not supported in yaml for now"))
}
) {
values = ProjectTemplate.ALL
isRequired = false
}
}
val initTemplate by pipelineTask(GenerationPhase.INIT_TEMPLATE) {
withAction {
val selectedTemplate = ProjectTemplatesPlugin::template.reference.notRequiredSettingValue
selectedTemplate?.setsValues?.forEach { (setting, value) ->
context.settingContext[setting] = value
}
UNIT_SUCCESS
}
fun SettingsWritingContext.applyProjectTemplate(projectTemplate: ProjectTemplate) {
projectTemplate.setsValues.forEach { (setting, value) ->
setting.setValue(value)
}
KotlinPlugin::modules.settingValue.withAllSubModules(includeSourcesets = true).forEach { module ->
module.apply { initDefaultValuesForSettings() }
}
}
@@ -35,6 +35,8 @@ sealed class ProjectTemplate : DisplayableSettingItem {
abstract val htmlDescription: String
abstract val suggestedProjectName: String
abstract val projectKind: ProjectKind
open val id: String
get() = this::class.simpleName.toString().removeSuffix("Template").removeSuffix("Project")
val tags: List<TemplateTag>
get() = setsPluginSettings
.firstOrNull { it.setting == KotlinPlugin::modules.reference }
@@ -74,8 +76,8 @@ sealed class ProjectTemplate : DisplayableSettingItem {
companion object {
val ALL = listOf(
CustomSingleplatformProjectTemplate,
CustomMultiplatformProjectTemplate,
EmptySingleplatformProjectTemplate,
EmptyMultiplatformProjectTemplate,
JvmConsoleApplication,
JvmServerJsClient,
MultiplatformLibrary,
@@ -84,6 +86,10 @@ sealed class ProjectTemplate : DisplayableSettingItem {
NativeConsoleApplication,
JsBrowserApplication
)
fun byId(id: String): ProjectTemplate? = ALL.firstOrNull {
it.id.equals(id, ignoreCase = true)
}
}
}
@@ -122,7 +128,7 @@ private fun ModuleType.createDefaultTarget(
name: String = this.name
) = MultiplatformTargetModule(name, defaultTarget, createDefaultSourcesets())
object CustomSingleplatformProjectTemplate : ProjectTemplate() {
object EmptySingleplatformProjectTemplate : ProjectTemplate() {
override val title = "Empty Kotlin/JVM Project"
override val htmlDescription = title
override val suggestedProjectName = "myKotlinJvmProject"
@@ -136,7 +142,7 @@ object CustomSingleplatformProjectTemplate : ProjectTemplate() {
)
}
object CustomMultiplatformProjectTemplate : ProjectTemplate() {
object EmptyMultiplatformProjectTemplate : ProjectTemplate() {
override val title = "Empty Kotlin Multiplatform Project"
override val htmlDescription = "Multiplatform Gradle project without predefined targets"
override val suggestedProjectName = "myKotlinMultiplatformProject"
@@ -234,7 +240,6 @@ object AndroidApplication : ProjectTemplate() {
KotlinPlugin::modules withValue listOf(
Module(
"app",
ModuleKind.singleplatformJvm,
AndroidSinglePlatformModuleConfigurator,
template = null,
sourcesets = SourcesetType.ALL.map { type ->
@@ -257,7 +262,6 @@ object NativeConsoleApplication : ProjectTemplate() {
KotlinPlugin::modules withValue listOf(
Module(
"app",
ModuleKind.multiplatform,
MppModuleConfigurator,
template = null,
sourcesets = emptyList(),
@@ -282,7 +286,6 @@ object JsBrowserApplication : ProjectTemplate() {
KotlinPlugin::modules withValue listOf(
Module(
"frontend",
ModuleKind.singleplatformJs,
JsSingleplatformModuleConfigurator,
template = SimpleJsClientTemplate(),
sourcesets = SourcesetType.ALL.map { type ->
@@ -308,7 +311,6 @@ object IOSApplication : ProjectTemplate() {
KotlinPlugin::modules withValue listOf(
Module(
"iosApp",
ModuleKind.singleplatformJvm,
IOSSinglePlatformModuleConfigurator,
template = null,
sourcesets = SourcesetType.ALL.map { type ->
@@ -26,8 +26,7 @@ enum class ModuleKind : DisplayableSettingItem {
// TODO separate to classes
class Module(
var name: String,
val kind: ModuleKind,
var configurator: ModuleConfigurator,
val configurator: ModuleConfigurator,
var template: Template?,
val sourcesets: List<Sourceset>,
subModules: List<Module>,
@@ -35,6 +34,9 @@ class Module(
override val identificator: Identificator = GeneratedIdentificator(name)
) : DisplayableSettingItem, Validatable<Module>, IdentificatorOwner {
val kind: ModuleKind
get() = configurator.moduleKind
override val validator: SettingValidator<Module> = settingValidator<Module> { module ->
StringValidators.shouldNotBeBlank("Module name").validate(this, module.name)
} and settingValidator { module ->
@@ -42,12 +44,13 @@ class Module(
} and settingValidator { module ->
withSettingsOf(module) {
allSettingsOfModuleConfigurator(configurator).map { setting ->
val value = when (setting) {
is PluginSetting<Any, SettingType<Any>> -> setting.reference.notRequiredSettingValue
is ModuleConfiguratorSetting<Any, SettingType<Any>> -> setting.reference.notRequiredSettingValue
val reference = when (setting) {
is PluginSetting<Any, SettingType<Any>> -> setting.reference
is ModuleConfiguratorSetting<Any, SettingType<Any>> -> setting.reference
else -> null
}
?: setting.savedOrDefaultValue
val value = reference?.notRequiredSettingValue
?: reference?.savedOrDefaultValue
?: return@map ValidationResult.ValidationError("${setting.title.capitalize()} should not be blank")
(setting.validator as SettingValidator<Any>).validate(this@settingValidator, value)
}.fold()
@@ -57,7 +60,7 @@ class Module(
org.jetbrains.kotlin.tools.projectWizard.templates.withSettingsOf(module) {
template.settings.map { setting ->
val value = setting.reference.notRequiredSettingValue
?: setting.savedOrDefaultValue
?: setting.reference.savedOrDefaultValue
?: return@map ValidationResult.ValidationError("${setting.title.capitalize()} should not be blank")
(setting.validator as SettingValidator<Any>).validate(this@settingValidator, value)
}.fold()
@@ -100,19 +103,14 @@ class Module(
val parser: Parser<Module> = mapParser { map, path ->
val (name) = map.parseValue<String>(path, "name")
val identificator = GeneratedIdentificator(name)
val (kind) = map.parseValue<ModuleKind>(this, path, "kind", enumParser())
val (configurator) = map.parseValue(this, path, "type", ModuleConfigurator.getParser(identificator))
val template = map["template"]?.let {
Template.parser(identificator).parse(this, it, "$path.template")
}.nullableValue()
val (sourcesets) = map.parseValue(
this,
path,
"sourcesets",
listParser(Sourceset.parser())
) { emptyList() }
val sourcesets = listOf(Sourceset(SourcesetType.main), Sourceset(SourcesetType.test))
val (submodules) = map.parseValue(this, path, "subModules", listParser(Module.parser)) { emptyList() }
Module(name, kind, configurator, template, sourcesets, submodules, identificator = identificator)
Module(name, configurator, template, sourcesets, submodules, identificator = identificator)
}
}
@@ -142,7 +140,6 @@ val Module.isRootModule
fun MultiplatformTargetModule(name: String, configurator: ModuleConfigurator, sourcesets: List<Sourceset>) =
Module(
name,
ModuleKind.target,
configurator,
null,
sourcesets,
@@ -153,7 +150,6 @@ fun MultiplatformTargetModule(name: String, configurator: ModuleConfigurator, so
fun MultiplatformModule(name: String, targets: List<Module> = emptyList()) =
Module(
name,
ModuleKind.multiplatform,
MppModuleConfigurator,
null,
emptyList(),
@@ -164,7 +160,6 @@ fun MultiplatformModule(name: String, targets: List<Module> = emptyList()) =
fun SingleplatformModule(name: String, sourcesets: List<Sourceset>) =
Module(
name,
ModuleKind.singleplatformJvm,
JvmSinglePlatformModuleConfigurator,
null,
sourcesets,
@@ -31,7 +31,7 @@ data class PathBasedSourcesetDependency(val path: ModulePath) : SourcesetDepende
// A `main` or `test` sourceset for single or multiplatform projects
class Sourceset(
val sourcesetType: SourcesetType,
var dependencies: List<SourcesetDependency>,
var dependencies: List<SourcesetDependency> = emptyList(),
var parent: Module? = null,
override val identificator: Identificator = GeneratedIdentificator(sourcesetType.name)
) : DisplayableSettingItem, IdentificatorOwner {
@@ -39,7 +39,7 @@ class SimpleJsClientTemplate : Template() {
|| module.configurator == JsSingleplatformModuleConfigurator
val renderEngine by enumSetting<RenderEngine>("Rendering engine", GenerationPhase.PROJECT_GENERATION) {
defaultValue = RenderEngine.REACT_WITH_STYLED
defaultValue = value(RenderEngine.REACT_WITH_STYLED)
}
override val settings: List<TemplateSetting<*, *>> = listOf(renderEngine)
@@ -78,8 +78,11 @@ abstract class Template : SettingsOwner, EntitiesOwnerDescriptor {
fun SettingsWritingContext.initDefaultValuesFor(module: Module) {
withSettingsOf(module) {
settings.forEach { setting ->
val defaultValue = setting.savedOrDefaultValue ?: return@forEach
setting.reference.setValue(defaultValue)
val reference = setting.reference
val defaultValue = reference.savedOrDefaultValue ?: return@forEach
if (reference.notRequiredSettingValue == null) {
reference.setValue(defaultValue)
}
}
}
}
@@ -3,33 +3,58 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard
import org.jetbrains.kotlin.tools.projectWizard.core.context.ReadingContext
import org.jetbrains.kotlin.tools.projectWizard.core.context.WritingContext
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.core.context.SettingsWritingContext
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
import org.jetbrains.kotlin.tools.projectWizard.core.service.WizardService
import org.jetbrains.kotlin.tools.projectWizard.core.service.ServicesManager
import org.jetbrains.kotlin.tools.projectWizard.core.service.SettingSavingWizardService
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.withAllSubModules
abstract class Wizard(createPlugins: PluginsCreator, val servicesManager: ServicesManager, private val isUnitTestMode: Boolean) {
val context = Context(createPlugins, EventManager())
val valuesReadingContext =
ReadingContext(context, servicesManager, isUnitTestMode)
val valuesReadingContext = ReadingContext(context, servicesManager, isUnitTestMode)
private val settingsWritingContext = SettingsWritingContext(context, servicesManager, isUnitTestMode)
protected val plugins = context.plugins
protected val pluginSettings = plugins.flatMap { it.declaredSettings }.distinctBy { it.path }
inline fun <reified V : Any> settingValue(setting: PluginSettingReference<V, SettingType<V>>): V =
with(valuesReadingContext) { setting.settingValue }
private fun Context.checkAllRequiredSettingPresent(phases: Set<GenerationPhase>): TaskResult<Unit> =
getUnspecifiedSettings(phases).let { unspecifiedSettings ->
if (unspecifiedSettings.isEmpty()) UNIT_SUCCESS
else Failure(RequiredSettingsIsNotPresentError(unspecifiedSettings.map { it.path }))
}
private fun initNonPluginDefaultValues() {
with(settingsWritingContext) {
KotlinPlugin::modules.reference.notRequiredSettingValue?.withAllSubModules(includeSourcesets = true)?.forEach { module ->
with(module) { initDefaultValuesForSettings() }
}
}
}
private fun initPluginDefaultValues() {
with(settingsWritingContext) {
for (setting in pluginSettings) {
if (setting.reference.notRequiredSettingValue == null) {
val defaultValue = setting.reference.savedOrDefaultValue ?: continue
setting.reference.setValue(defaultValue)
}
}
}
}
@Suppress("UNUSED_PARAMETER")
fun ReadingContext.validate(phases: Set<GenerationPhase>): TaskResult<Unit> =
context.settingContext.allPluginSettings.map { setting ->
val value = context.settingContext.pluginSettingValue(setting) ?: return@map ValidationResult.OK
when (setting.neededAtPhase) {
in phases -> (setting.validator as SettingValidator<Any>).validate(this, value)
else -> ValidationResult.OK
}
if (setting.neededAtPhase in phases && setting.isActive(this))
(setting.validator as SettingValidator<Any>).validate(this, value)
else ValidationResult.OK
}.fold(ValidationResult.OK, ValidationResult::and).toResult()
private fun ReadingContext.saveSettingValues(phases: Set<GenerationPhase>) {
@@ -50,12 +75,10 @@ abstract class Wizard(createPlugins: PluginsCreator, val servicesManager: Servic
phases: Set<GenerationPhase>,
onTaskExecuting: (PipelineTask) -> Unit = {}
): TaskResult<Unit> = computeM {
initPluginDefaultValues()
initNonPluginDefaultValues()
context.checkAllRequiredSettingPresent(phases).ensure()
val taskRunningContext = WritingContext(
context,
servicesManager.withServices(services),
isUnitTestMode
)
val taskRunningContext = WritingContext(context, servicesManager.withServices(services), isUnitTestMode)
taskRunningContext.validate(phases).ensure()
taskRunningContext.saveSettingValues(phases)
val (tasksSorted) = context.sortTasks().map { tasks ->