Wizard: Add initial version of the new project wizard
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api("org.apache.velocity:velocity:1.7") // we have to use the old version as it is the same as bundled into IntelliJ
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
|
||||
testImplementation(project(":kotlin-test:kotlin-test-junit"))
|
||||
testImplementation(commonDep("junit:junit"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
|
||||
testsJar()
|
||||
@@ -0,0 +1,29 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlinStdlib())
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
|
||||
implementation(project(":libraries:tools:new-project-wizard"))
|
||||
compileOnly(intellijDep()) { includeJars("snakeyaml-1.24") }
|
||||
|
||||
testImplementation(projectTests(":compiler:tests-common"))
|
||||
testImplementation(project(":kotlin-test:kotlin-test-junit"))
|
||||
testImplementation(commonDep("junit:junit"))
|
||||
testImplementation(intellijDep())
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
|
||||
testsJar()
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard
|
||||
|
||||
import YamlParsingError
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.yaml.snakeyaml.Yaml
|
||||
import org.yaml.snakeyaml.parser.ParserException
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class YamlSettingsParser(settings: List<PluginSetting<Any, *>>, private val parsingState: ParsingState) {
|
||||
private val settingByName = settings.associateBy { it.path }
|
||||
|
||||
fun parseYamlText(yaml: String): TaskResult<Map<SettingReference<*, *>, Any>> {
|
||||
val yamlObject = try {
|
||||
Success(Yaml().load<Any>(yaml) ?: emptyMap<String, Any>())
|
||||
} catch (e: ParserException) {
|
||||
Failure(YamlParsingError(e))
|
||||
} catch (e: Exception) {
|
||||
Failure(ExceptionErrorImpl(e))
|
||||
}
|
||||
return yamlObject.flatMap { map ->
|
||||
if (map is Map<*, *>) {
|
||||
val result = ComputeContext.runInComputeContextWithState(parsingState) {
|
||||
parseSettingValues(map, "")
|
||||
}
|
||||
result.map { (pluginSettings, newState) ->
|
||||
pluginSettings + newState.settingValues
|
||||
}
|
||||
} else Failure(
|
||||
BadSettingValueError("Settings file should be a map of settings")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun parseYamlFile(file: Path) = computeM {
|
||||
val (yaml) = safe { file.toFile().readText() }
|
||||
parseYamlText(yaml)
|
||||
}
|
||||
|
||||
private fun ParsingContext.parseSettingValues(
|
||||
data: Any,
|
||||
settingPath: String
|
||||
): TaskResult<Map<PluginSettingReference<*, *>, Any>> =
|
||||
if (settingPath in settingByName) compute {
|
||||
val setting = settingByName.getValue(settingPath)
|
||||
val (parsed) = setting.type.parse(this, data, settingPath)
|
||||
listOf(PluginSettingReference(setting) to parsed)
|
||||
} else {
|
||||
when (data) {
|
||||
is Map<*, *> -> {
|
||||
data.entries.mapCompute { (name, value) ->
|
||||
if (value == null) fail(BadSettingValueError("No value was found for a key `$settingPath`"))
|
||||
val prefix = if (settingPath.isEmpty()) "" else "$settingPath."
|
||||
val (children) = parseSettingValues(value, "$prefix$name")
|
||||
children.entries.map { (key, value) -> key to value }
|
||||
}.sequence().map { it.flatten() }
|
||||
}
|
||||
else -> Failure(
|
||||
BadSettingValueError("No value was found for a key `$settingPath`")
|
||||
)
|
||||
}
|
||||
}.map { it.toMap() }
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
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.entity.PipelineTask
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.PluginSettingReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.AndroidServiceImpl
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.Service
|
||||
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
|
||||
|
||||
class YamlWizard(
|
||||
private val yaml: String,
|
||||
private val path: String,
|
||||
createPlugins: (Context) -> List<Plugin>
|
||||
) : Wizard(createPlugins, listOf(AndroidServiceImpl())) {
|
||||
override fun apply(
|
||||
services: List<Service>,
|
||||
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 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()
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Error
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.asString
|
||||
import org.yaml.snakeyaml.parser.ParserException
|
||||
|
||||
data class YamlParsingError(val exception: ParserException) : Error() {
|
||||
override val message: String
|
||||
get() = exception.asString()
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-android-extensions'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
applicationId 'testArtifactId'
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 29
|
||||
versionCode 1
|
||||
versionName '1.0'
|
||||
}
|
||||
buildTypes {
|
||||
'release' {
|
||||
isMinifyEnabled false
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.1.0'
|
||||
implementation kotlin('stdlib-jdk7')
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation kotlin('stdlib-jdk8')
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("kotlin-android")
|
||||
id("kotlin-android-extensions")
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
android {
|
||||
compileSdkVersion(29)
|
||||
defaultConfig {
|
||||
applicationId = "testArtifactId"
|
||||
minSdkVersion(24)
|
||||
targetSdkVersion(29)
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
getByName("release") {
|
||||
isMinifyEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation("androidx.appcompat:appcompat:1.1.0")
|
||||
implementation("androidx.core:core-ktx:1.1.0")
|
||||
implementation(kotlin("stdlib-jdk7"))
|
||||
implementation("androidx.constraintlayout:constraintlayout:1.1.3")
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.2.1'
|
||||
classpath kotlin('gradle-plugin', '1.3.61')
|
||||
}
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:3.2.1")
|
||||
classpath(kotlin("gradle-plugin", "1.3.61"))
|
||||
}
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type:
|
||||
name: android
|
||||
androidSdkPath: /home/ilya/Android/Sdk
|
||||
kind: singleplatform
|
||||
name: android
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
js('nodeJs') {
|
||||
nodejs {
|
||||
|
||||
}
|
||||
}
|
||||
js('browser') {
|
||||
browser {
|
||||
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
nodeJsMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-js')
|
||||
}
|
||||
}
|
||||
nodeJsTest {
|
||||
|
||||
}
|
||||
browserMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-js')
|
||||
}
|
||||
}
|
||||
browserTest {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
js("nodeJs") {
|
||||
nodejs {
|
||||
|
||||
}
|
||||
}
|
||||
js("browser") {
|
||||
browser {
|
||||
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
val nodeJsMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-js"))
|
||||
}
|
||||
}
|
||||
val nodeJsTest by getting
|
||||
val browserMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-js"))
|
||||
}
|
||||
}
|
||||
val browserTest by getting
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type: multiplatform
|
||||
kind: multiplatform
|
||||
name: a
|
||||
subModules:
|
||||
- type: jsNode
|
||||
kind: target
|
||||
name: nodeJs
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
- type: jsBrowser
|
||||
kind: target
|
||||
name: browser
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
jvm()
|
||||
sourceSets {
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-jdk8')
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
jvm()
|
||||
sourceSets {
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
}
|
||||
val jvmTest by getting
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type: multiplatform
|
||||
kind: multiplatform
|
||||
name: a
|
||||
subModules:
|
||||
- type: jvmTarget
|
||||
kind: target
|
||||
name: jvm
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-jdk8')
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.MavenPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?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>nya</artifactId>
|
||||
<groupId>testGroupId</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>nya</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.code.style>official</kotlin.code.style>
|
||||
</properties>
|
||||
|
||||
<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-stdlib</artifactId>
|
||||
<version>1.3.61</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Singleplatform
|
||||
modules:
|
||||
- type: JVM Module
|
||||
name: nya
|
||||
kind: singleplatform
|
||||
sourcesets:
|
||||
- type: main
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
def hostOs = System.getProperty("os.name")
|
||||
def isMingwX64 = hostOs.startsWith("Windows")
|
||||
org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTests nativeTarget
|
||||
if (hostOs == "Mac OS X") nativeTarget = macosX64('myNative')
|
||||
else if (hostOs == "Linux") nativeTarget = linuxX64("myNative")
|
||||
else if (isMingwX64) return nativeTarget = mingwX64("myNative")
|
||||
else throw new GradleException("Host OS is not supported in Kotlin/Native.")
|
||||
|
||||
nativeTarget.with {
|
||||
binaries {
|
||||
executable {
|
||||
entryPoint = 'MAIN CLASS'
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
myNativeMain {
|
||||
|
||||
}
|
||||
myNativeTest {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
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("myNative")
|
||||
hostOs == "Linux" -> linuxX64("myNative")
|
||||
isMingwX64 -> mingwX64("myNative")
|
||||
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
|
||||
}
|
||||
|
||||
nativeTarget.apply {
|
||||
binaries {
|
||||
executable {
|
||||
entryPoint = "MAIN CLASS"
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
val myNativeMain by getting
|
||||
val myNativeTest by getting
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type: multiplatform
|
||||
kind: multiplatform
|
||||
name: a
|
||||
subModules:
|
||||
- type: nativeForCurrentSystem
|
||||
kind: target
|
||||
name: myNative
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
jvm()
|
||||
js('a') {
|
||||
browser {
|
||||
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-jdk8')
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
|
||||
}
|
||||
aMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-js')
|
||||
}
|
||||
}
|
||||
aTest {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
jvm()
|
||||
js("a") {
|
||||
browser {
|
||||
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
}
|
||||
val jvmTest by getting
|
||||
val aMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-js"))
|
||||
}
|
||||
}
|
||||
val aTest by getting
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type: multiplatform
|
||||
kind: multiplatform
|
||||
name: a
|
||||
subModules:
|
||||
- type: jvmTarget
|
||||
kind: target
|
||||
name: jvm
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
- type: jsBrowser
|
||||
kind: target
|
||||
name: a
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
linuxX64 {
|
||||
binaries {
|
||||
executable {
|
||||
entryPoint = 'MAIN CLASS'
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
linuxX64Main {
|
||||
|
||||
}
|
||||
linuxX64Test {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
linuxX64 {
|
||||
binaries {
|
||||
executable {
|
||||
entryPoint = "MAIN CLASS"
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
val linuxX64Main by getting
|
||||
val linuxX64Test by getting
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type: multiplatform
|
||||
kind: multiplatform
|
||||
name: a
|
||||
subModules:
|
||||
- type: linuxX64Target
|
||||
kind: target
|
||||
name: linuxX64
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.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.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 java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
abstract class AbstractBuildFileGenerationTest : AbstractPluginBasedTest() {
|
||||
|
||||
fun doTest(directoryPath: String) {
|
||||
val directory = Paths.get(directoryPath)
|
||||
val testData = init(directory)
|
||||
if (KotlinDslPlugin::class in testData.pluginClasses) {
|
||||
doTest(directory, testData, BuildSystem.GRADLE_KOTLIN_DSL)
|
||||
}
|
||||
if (GroovyDslPlugin::class in testData.pluginClasses) {
|
||||
doTest(directory, testData, BuildSystem.GRADLE_GROOVY_DSL)
|
||||
}
|
||||
if (MavenPlugin::class in testData.pluginClasses) {
|
||||
doTest(directory, testData, BuildSystem.MAVEN)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doTest(directory: Path, testData: WizardTestData, 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(), testData.createPlugins)
|
||||
val result = wizard.apply(Services.osServices, GenerationPhase.ALL)
|
||||
result.onFailure { errors ->
|
||||
errors.forEach { error ->
|
||||
if (error is ExceptionError) {
|
||||
throw error.exception
|
||||
}
|
||||
}
|
||||
fail(errors.joinToString("\n"))
|
||||
}
|
||||
|
||||
val expectedDirectory = (directory / EXPECTED_DIRECTORY_NAME).takeIf { Files.exists(it) } ?: directory
|
||||
|
||||
compareFiles(
|
||||
expectedDirectory.allBuildFiles(buildSystem), expectedDirectory,
|
||||
tempDir.allBuildFiles(buildSystem), tempDir
|
||||
)
|
||||
}
|
||||
|
||||
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()
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EXPECTED_DIRECTORY_NAME = "expected"
|
||||
|
||||
private val defaultStructure =
|
||||
"""structure:
|
||||
name: generatedProject
|
||||
groupId: testGroupId
|
||||
artifactId: testArtifactId
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.UsefulTestCase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Context
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Plugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.PluginReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.StructurePlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin
|
||||
import java.nio.file.Path
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
abstract class AbstractPluginBasedTest : UsefulTestCase() {
|
||||
open val defaultPlugins: List<PluginReference> = listOf(
|
||||
StructurePlugin::class,
|
||||
TemplatesPlugin::class
|
||||
)
|
||||
|
||||
protected fun init(directory: Path): WizardTestData {
|
||||
val pluginNames = directory.resolve("plugins.txt").toFile().readLines().mapNotNull { name ->
|
||||
name.trim().takeIf { it.isNotBlank() }
|
||||
}.distinct()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val pluginClasses = defaultPlugins + pluginNames.map { pluginName ->
|
||||
Class.forName(pluginName).kotlin as PluginReference
|
||||
}
|
||||
|
||||
val createPlugins = { context: Context ->
|
||||
pluginClasses.map { pluginClass ->
|
||||
pluginClass.primaryConstructor!!.call(context)
|
||||
}
|
||||
}
|
||||
return WizardTestData(pluginClasses, createPlugins)
|
||||
}
|
||||
}
|
||||
|
||||
data class WizardTestData(
|
||||
val pluginClasses: List<PluginReference>,
|
||||
val createPlugins: (Context) -> List<Plugin>
|
||||
)
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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/buildFileGeneration")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class BuildFileGenerationTestGenerated extends AbstractBuildFileGenerationTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInBuildFileGeneration() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration"), Pattern.compile("^([^\\.]+)$"), false);
|
||||
}
|
||||
|
||||
@TestMetadata("android")
|
||||
public void testAndroid() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/android/");
|
||||
}
|
||||
|
||||
@TestMetadata("jsNodeAndBrowserTargets")
|
||||
public void testJsNodeAndBrowserTargets() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/jsNodeAndBrowserTargets/");
|
||||
}
|
||||
|
||||
@TestMetadata("jvmTarget")
|
||||
public void testJvmTarget() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/jvmTarget/");
|
||||
}
|
||||
|
||||
@TestMetadata("kotlinJvm")
|
||||
public void testKotlinJvm() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/kotlinJvm/");
|
||||
}
|
||||
|
||||
@TestMetadata("nativeForCurrentSystem")
|
||||
public void testNativeForCurrentSystem() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/nativeForCurrentSystem/");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleMultiplatform")
|
||||
public void testSimpleMultiplatform() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/simpleMultiplatform/");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleNativeTarget")
|
||||
public void testSimpleNativeTarget() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/simpleNativeTarget/");
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.hamcrest.core.Is
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.junit.Assert.assertThat
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.stream.Collectors
|
||||
|
||||
internal fun Path.readFile() = toFile().readText().trim()
|
||||
|
||||
internal fun Path.listFiles(filter: (Path) -> Boolean) =
|
||||
Files.walk(this).filter { path ->
|
||||
Files.isRegularFile(path) && filter(path)
|
||||
}.collect(Collectors.toList()).sorted()
|
||||
|
||||
internal fun compareFiles(
|
||||
expectedFiles: List<Path>, expectedDir: Path,
|
||||
actualFiles: List<Path>, actualDir: Path
|
||||
) {
|
||||
val expectedFilesSorted = expectedFiles.sorted()
|
||||
val actualFilesSorted = actualFiles.sorted()
|
||||
|
||||
assertThat(
|
||||
actualFilesSorted.map { actualDir.relativize(it) },
|
||||
Is.`is`(expectedFilesSorted.map { expectedDir.relativize(it) })
|
||||
)
|
||||
|
||||
for ((actualFile, expectedFile) in actualFilesSorted zip expectedFilesSorted) {
|
||||
KotlinTestUtils.assertEqualsToFile(expectedFile.toFile(), actualFile.readFile())
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="${package.asCodePackage()}">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:supportsRtl="true">
|
||||
<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>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package ${package.asCodePackage()}
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun main(args: Array<String>) {
|
||||
println("Hello World!")
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-${version}-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
#foreach($property in $properties)
|
||||
${property.getFirst()}=${property.getSecond()}
|
||||
#end
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
#foreach($property in $properties)
|
||||
${property.getFirst()}=${property.getSecond()}
|
||||
#end
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
rootProject.name = "${projectName}"
|
||||
|
||||
#foreach($subProject in $subProjects)
|
||||
include("${subProject}")
|
||||
#end
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class Test {
|
||||
@Test
|
||||
fun testIt() {
|
||||
assertTrue(true)
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.RandomIdGenerator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.safeAs
|
||||
|
||||
|
||||
sealed class Identificator {
|
||||
abstract val id: String
|
||||
|
||||
final override fun equals(other: Any?): Boolean = other.safeAs<Identificator>()?.id == id
|
||||
final override fun hashCode(): Int = id.hashCode()
|
||||
final override fun toString(): String = id
|
||||
}
|
||||
|
||||
class GeneratedIdentificator(prefix: String?) : Identificator() {
|
||||
override val id = """${prefix.orEmpty()}_${RandomIdGenerator.generate()}"""
|
||||
}
|
||||
|
||||
|
||||
interface IdentificatorOwner {
|
||||
val identificator: Identificator
|
||||
}
|
||||
|
||||
val IdentificatorOwner.id
|
||||
get() = identificator.id
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.EntitiesOwnerDescriptor
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Parser
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
|
||||
interface SettingsOwner : EntitiesOwnerDescriptor {
|
||||
fun <V : Any, T : SettingType<V>> settingDelegate(
|
||||
create: (path: String) -> SettingBuilder<V, T>
|
||||
): ReadOnlyProperty<Any, Setting<V, T>>
|
||||
|
||||
// the functions to create different kinds of settings
|
||||
// for now should be overridden with specific settings types :(
|
||||
// TODO think a way yo emulate higher-kind types
|
||||
|
||||
fun <V : DisplayableSettingItem> dropDownSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: DropDownSettingType.Builder<V>.() -> Unit = {}
|
||||
): ReadOnlyProperty<Any, Setting<V, DropDownSettingType<V>>> = settingDelegate { path ->
|
||||
DropDownSettingType.Builder(path, title, neededAtPhase, parser).apply(init)
|
||||
}
|
||||
|
||||
fun stringSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: StringSettingType.Builder.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
StringSettingType.Builder(path, title, neededAtPhase).apply(init)
|
||||
}
|
||||
|
||||
fun booleanSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: BooleanSettingType.Builder.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
BooleanSettingType.Builder(path, title, neededAtPhase).apply(init)
|
||||
}
|
||||
|
||||
fun <V : Any> valueSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: ValueSettingType.Builder<V>.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
ValueSettingType.Builder(path, title, neededAtPhase, parser).apply(init)
|
||||
}
|
||||
|
||||
fun versionSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: VersionSettingType.Builder.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
VersionSettingType.Builder(path, title, neededAtPhase).apply(init)
|
||||
}
|
||||
|
||||
fun <V : Any> listSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: ListSettingType.Builder<V>.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
ListSettingType.Builder(path, title, neededAtPhase, parser).apply(init)
|
||||
}
|
||||
|
||||
fun pathSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: PathSettingType.Builder.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
PathSettingType.Builder(path, title, neededAtPhase).apply(init)
|
||||
}
|
||||
}
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingType
|
||||
|
||||
|
||||
sealed class CheckerRule {
|
||||
abstract fun check(context: ValuesReadingContext): Boolean
|
||||
}
|
||||
|
||||
data class RuleBySettingValue(
|
||||
val settingReference: SettingReference<Any, SettingType<Any>>,
|
||||
val expectedValue: Any
|
||||
) : CheckerRule() {
|
||||
override fun check(context: ValuesReadingContext): Boolean = with(context) {
|
||||
settingReference.notRequiredSettingValue() == expectedValue
|
||||
}
|
||||
}
|
||||
|
||||
data class OrRule(
|
||||
val left: CheckerRule,
|
||||
val right: CheckerRule
|
||||
) : CheckerRule() {
|
||||
override fun check(context: ValuesReadingContext): Boolean = left.check(context) || right.check(context)
|
||||
}
|
||||
|
||||
data class Checker(val rules: List<CheckerRule>) {
|
||||
fun check(context: ValuesReadingContext) =
|
||||
rules.all { rule -> rule.check(context) }
|
||||
|
||||
class Builder {
|
||||
private val rules = mutableListOf<CheckerRule>()
|
||||
|
||||
infix fun <V : Any, T: SettingType<V>> SettingReference<V, T>.shouldBeEqual(value: V) =
|
||||
RuleBySettingValue(this, value)
|
||||
|
||||
infix fun CheckerRule.or(other: CheckerRule) =
|
||||
OrRule(this, other)
|
||||
|
||||
fun extend(parent: Checker) {
|
||||
this.rules += parent.rules
|
||||
}
|
||||
|
||||
fun rule(rule: CheckerRule) {
|
||||
rules += rule
|
||||
}
|
||||
|
||||
fun build() = Checker(rules)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ALWAYS_AVAILABLE = Checker(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
fun checker(init: Checker.Builder.() -> Unit) =
|
||||
Checker.Builder().apply(init).build()
|
||||
|
||||
|
||||
interface ContextOwner {
|
||||
val context: Context
|
||||
}
|
||||
|
||||
interface ActivityCheckerOwner {
|
||||
val activityChecker: Checker
|
||||
|
||||
fun isActive(valuesReadingContext: ValuesReadingContext) = activityChecker.check(valuesReadingContext)
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
import kotlin.reflect.full.memberProperties
|
||||
|
||||
class Context(private val pluginsCreator: PluginsCreator, val eventManager: EventManager) {
|
||||
private fun initPlugin(plugin: Plugin) {
|
||||
for (entityReference in plugin::class.memberProperties) {
|
||||
val type = entityReference.returnType.classifier.safeAs<KClass<*>>() ?: continue
|
||||
if (type.isSubclassOf(Entity::class)) {
|
||||
when (val entity = entityReference.getter.call(plugin)) {
|
||||
is Property<*> -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
propertyContext[entityReference as PropertyReference<Any>] = entity.defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val settingContext = SettingContext(eventManager::fireListeners)
|
||||
val propertyContext = PropertyContext()
|
||||
val taskContext = TaskContext()
|
||||
val plugins = pluginsCreator(this).onEach(::initPlugin)
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun getUnspecifiedSettings(phases: Set<GenerationPhase>): List<AnySetting> {
|
||||
val required = plugins
|
||||
.flatMap { plugin ->
|
||||
plugin.declaredSettings.mapNotNull { setting ->
|
||||
if (setting.neededAtPhase !in phases) return@mapNotNull null
|
||||
if (setting.isRequired) setting else null
|
||||
}
|
||||
}.toSet()
|
||||
val provided = settingContext.allPluginSettings.map { it.path }.toSet()
|
||||
return required.filterNot { it.path in provided }
|
||||
}
|
||||
|
||||
private val pipelineLineTasks: List<PipelineTask>
|
||||
get() = plugins
|
||||
.flatMap { it.declaredTasks.filterIsInstance<PipelineTask>() }
|
||||
|
||||
private fun task(reference: PipelineTaskReference) =
|
||||
taskContext.getEntity(reference) as? PipelineTask
|
||||
?: error(reference.path)
|
||||
|
||||
private val dependencyList: Map<PipelineTask, List<PipelineTask>>
|
||||
get() {
|
||||
val dependeeMap = pipelineLineTasks.flatMap { task ->
|
||||
task.after.map { after -> task to task(after) }
|
||||
}
|
||||
|
||||
val dependencyMap = pipelineLineTasks.flatMap { task ->
|
||||
task.before.map { before -> task(before) to task }
|
||||
}
|
||||
|
||||
return (dependeeMap + dependencyMap)
|
||||
.groupBy { it.first }
|
||||
.mapValues {
|
||||
it.value.map { it.second }
|
||||
}
|
||||
}
|
||||
|
||||
fun copy() = Context(pluginsCreator, eventManager)
|
||||
|
||||
fun sortTasks(): TaskResult<List<PipelineTask>> =
|
||||
TaskSorter().sort(pipelineLineTasks, dependencyList)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import java.nio.file.Paths
|
||||
|
||||
object Defaults {
|
||||
val SRC_DIR = Paths.get("src")
|
||||
val KOTLIN_DIR = Paths.get("kotlin")
|
||||
val RESOURCES_DIR = Paths.get("resources")
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
interface EntitiesOwnerDescriptor {
|
||||
val id: String
|
||||
}
|
||||
|
||||
interface EntitiesOwner<D : EntitiesOwnerDescriptor> {
|
||||
val descriptor: D
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
|
||||
|
||||
class EventManager {
|
||||
private val listeners = mutableListOf<(SettingReference<*, *>?) -> Unit>()
|
||||
|
||||
fun addSettingUpdaterEventListener(listener: (SettingReference<*, *>?) -> Unit) {
|
||||
listeners += listener
|
||||
}
|
||||
|
||||
fun fireListeners(reference: SettingReference<*, *>?) {
|
||||
listeners.forEach { it(reference) }
|
||||
}
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.templates.Template
|
||||
import java.nio.file.Paths
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
data class ParsingState(
|
||||
val idToTemplate: Map<String, Template>,
|
||||
val settingValues: Map<SettingReference<*, *>, Any>
|
||||
) : ComputeContextState
|
||||
|
||||
fun ParsingState.withSettings(newSettings: List<Pair<SettingReference<*, *>, Any>>) =
|
||||
copy(settingValues = settingValues + newSettings)
|
||||
|
||||
typealias ParsingContext = ComputeContext<ParsingState>
|
||||
|
||||
abstract class Parser<out T : Any> {
|
||||
abstract fun ParsingContext.parse(value: Any?, path: String): TaskResult<T>
|
||||
}
|
||||
|
||||
fun <T : Any> alwaysFailingParser() = object : Parser<T>() {
|
||||
override fun ParsingContext.parse(value: Any?, path: String): TaskResult<T> =
|
||||
Failure(object : Error() {
|
||||
override val message: String
|
||||
get() = "Should not be called"
|
||||
})
|
||||
}
|
||||
|
||||
fun <T : Any> Parser<T>.parse(context: ParsingContext, value: Any?, path: String) =
|
||||
with(context) { parse(value, path) }
|
||||
|
||||
inline fun <reified E : Enum<E>> enumParser(): Parser<E> = object : Parser<E>() {
|
||||
override fun ParsingContext.parse(value: Any?, path: String): TaskResult<E> = computeM {
|
||||
val (enumName) = value.parseAs<String>(path)
|
||||
safe { enumValueOf<E>(enumName) }.mapFailure {
|
||||
listOf(
|
||||
ParseError(
|
||||
"For setting `$path` one of [${enumValues<E>().joinToString { it.name }}] was expected but `$enumName` was found"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified R : Any> listParser(
|
||||
elementParser: Parser<R>
|
||||
) = object : Parser<List<R>>() {
|
||||
override fun ParsingContext.parse(value: Any?, path: String): TaskResult<List<R>> = computeM {
|
||||
val (list) = value.parseAs<List<*>>(path)
|
||||
list.mapComputeM { elementParser.parse(this, it, path) }.sequence()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun <T : Any> mapParser(parseMap: suspend ParsingContext.(map: Map<String, *>, path: String) -> T) =
|
||||
object : Parser<T>() {
|
||||
override fun ParsingContext.parse(value: Any?, path: String): TaskResult<T> = when (value) {
|
||||
is String -> value.parseAs<String, T>(this, path) {
|
||||
parseMap(emptyMap<String, Any?>(), path).asSuccess()
|
||||
}
|
||||
else -> value.parseAs<Map<String, *>, T>(this, path) {
|
||||
parseMap(it, path).asSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun <T : Any> valueParser(parser: suspend ParsingContext.(value: Any?, path: String) -> T) =
|
||||
object : Parser<T>() {
|
||||
override fun ParsingContext.parse(value: Any?, path: String): TaskResult<T> = compute {
|
||||
parser(value, path)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : Any> valueParserM(parser: suspend ParsingContext.(value: Any?, path: String) -> TaskResult<T>) =
|
||||
object : Parser<T>() {
|
||||
override fun ParsingContext.parse(value: Any?, path: String): TaskResult<T> = computeM {
|
||||
parser(value, path)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> valueParser() = valueParser { value, path ->
|
||||
value.parseAs(path, T::class).get()
|
||||
}
|
||||
|
||||
|
||||
class DisjunctionParser<T : Any>(
|
||||
private val keyToParser: Map<String, Parser<T>>
|
||||
) : Parser<T>() {
|
||||
constructor(vararg keyToParser: Pair<String, Parser<T>>) : this(keyToParser.toMap())
|
||||
|
||||
override fun ParsingContext.parse(value: Any?, path: String): TaskResult<T> = when (value) {
|
||||
is String -> // consider string value as an empty map
|
||||
mapOf(value to emptyMap<Any?, Any?>()).asSuccess()
|
||||
else -> value.parseAs<Map<*, *>>(path)
|
||||
}.flatMap { map ->
|
||||
computeM {
|
||||
val (singleItem) = map.entries.singleOrNull()
|
||||
?.takeIf { it.key is String }
|
||||
.toResult { ParseError("Setting `$path` should contain a single-key value") }
|
||||
val (parser) = keyToParser[singleItem.key as String]
|
||||
.toResult { ParseError("`$path` should be one of [${keyToParser.keys.joinToString()}]") }
|
||||
parser.parse(this@parse, singleItem.value, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CollectingParser<T : Any>(
|
||||
private val keyToParser: Map<String, Parser<T>>
|
||||
) : Parser<List<T>>() {
|
||||
override fun ParsingContext.parse(value: Any?, path: String): TaskResult<List<T>> =
|
||||
value.parseAs<Map<*, *>, List<T>>(this, path) { map ->
|
||||
map.mapNotNull { (key, value) ->
|
||||
val parser = keyToParser[key] ?: return@mapNotNull null
|
||||
parser.parse(this, value, "$path.$key")
|
||||
}.sequence()
|
||||
}
|
||||
}
|
||||
|
||||
fun Any?.classMismatchError(path: String, expected: KClass<*>): ParseError {
|
||||
val classpath = this?.let { it::class.simpleName } ?: "null"
|
||||
return ParseError("Expected ${expected.simpleName!!} for `$path` but $classpath was found")
|
||||
}
|
||||
|
||||
inline fun <reified V : Any> Any?.parseAs(path: String) =
|
||||
safeAs<V>().toResult { classMismatchError(path, V::class) }
|
||||
|
||||
|
||||
inline fun <reified T : Any> Any?.parseAs(path: String, klass: KClass<T>): TaskResult<T> =
|
||||
this?.takeIf { it::class.isSubclassOf(klass) }?.safeAs<T>()
|
||||
.toResult { classMismatchError(path, klass) }
|
||||
|
||||
|
||||
inline fun <reified V : Any> Map<*, *>.parseValue(path: String, name: String) =
|
||||
get(name).parseAs<V>("$path.$name")
|
||||
|
||||
inline fun <reified V : Any> Map<*, *>.parseValue(path: String, name: String, defaultValue: (() -> V)) =
|
||||
get(name)?.parseAs<V>("$path.$name") ?: defaultValue().asSuccess()
|
||||
|
||||
|
||||
inline fun <reified V : Any, R : Any> Map<*, *>.parseValue(
|
||||
context: ParsingContext,
|
||||
path: String,
|
||||
name: String,
|
||||
crossinline parser: suspend ParsingContext.(V) -> TaskResult<R>
|
||||
) = with(context) {
|
||||
computeM {
|
||||
val (result) = get(path).parseAs<V>("$path.$name")
|
||||
parser(result)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> Map<*, *>.parseValue(
|
||||
path: String,
|
||||
name: String,
|
||||
klass: KClass<T>
|
||||
) = get(path).parseAs("$path.$name", klass)
|
||||
|
||||
|
||||
fun <R : Any> Map<*, *>.parseValue(
|
||||
context: ParsingContext,
|
||||
path: String,
|
||||
name: String,
|
||||
parser: Parser<R>,
|
||||
defaultValue: (() -> R)? = null
|
||||
) = with(context) {
|
||||
computeM {
|
||||
when (val value = get(name)) {
|
||||
null -> defaultValue?.invoke()?.asSuccess() ?: parser.parse(this, value = null, path = "$path.$name")
|
||||
else -> parser.parse(this, value, "$path.$name")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified V : Any, R : Any> Any?.parseAs(
|
||||
context: ParsingContext,
|
||||
path: String,
|
||||
crossinline parser: suspend ParsingContext.(V) -> TaskResult<R>
|
||||
) = with(context) {
|
||||
computeM {
|
||||
val (result) = this@parseAs.parseAs<V>(path)
|
||||
parser(result)
|
||||
}
|
||||
}
|
||||
|
||||
val pathParser = valueParser { value, path ->
|
||||
value.parseAs<String>(path).map { Paths.get(it) }.get()
|
||||
}
|
||||
|
||||
infix fun <V: Any> Parser<V>.or(alternative: Parser<V>): Parser<V> = object : Parser<V>() {
|
||||
override fun ParsingContext.parse(value: Any?, path: String): TaskResult<V> =
|
||||
this@or.parse(this, value, path).recover { alternative.parse(this, value, path) }
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.SettingsOwner
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||
import java.nio.file.Path
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
import kotlin.reflect.full.memberProperties
|
||||
|
||||
typealias PluginReference = KClass<out Plugin>
|
||||
typealias PluginsCreator = (Context) -> List<Plugin>
|
||||
|
||||
|
||||
abstract class Plugin(override val context: Context) : EntityBase(),
|
||||
SettingsOwner,
|
||||
ContextOwner,
|
||||
EntitiesOwner<Plugin> {
|
||||
override val descriptor get() = this
|
||||
override val id: String get() = path
|
||||
|
||||
override fun <V : Any, T : SettingType<V>> settingDelegate(
|
||||
create: (path: String) -> SettingBuilder<V, T>
|
||||
): ReadOnlyProperty<Any, PluginSetting<V, T>> = object : ReadOnlyProperty<Any, PluginSetting<V, T>> {
|
||||
override fun getValue(thisRef: Any, property: KProperty<*>): PluginSetting<V, T> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val reference = property as PluginSettingPropertyReference<V, T>
|
||||
context.settingContext.getPluginSetting(reference)?.let { return it }
|
||||
val setting = PluginSetting(create(reference.path).buildInternal())
|
||||
context.settingContext.setPluginSetting(reference, setting)
|
||||
return setting
|
||||
}
|
||||
}
|
||||
|
||||
val reference = this::class
|
||||
override val path = reference.path
|
||||
open val title: String = reference.name
|
||||
|
||||
val declaredSettings get() = entitiesOfType<PluginSetting<*, *>>().toSet()
|
||||
val declaredTasks get() = entitiesOfType<Task>()
|
||||
|
||||
|
||||
private inline fun <reified E : Entity> entitiesOfType() =
|
||||
reference.memberProperties.filter { kProperty ->
|
||||
kProperty.returnType.classifier.safeAs<KClass<*>>()?.isSubclassOf(E::class) == true
|
||||
}.mapNotNull { kProperty ->
|
||||
kProperty.safeAs<EntityReference>()?.getter?.call(this) as? E
|
||||
}
|
||||
|
||||
fun pipelineTask(phase: GenerationPhase, init: PipelineTask.Builder.() -> Unit) =
|
||||
PipelineTask.delegate(context.taskContext, phase, init)
|
||||
|
||||
fun <A, B : Any> task1(init: Task1.Builder<A, B>.() -> Unit) =
|
||||
Task1.delegate(context.taskContext, init)
|
||||
|
||||
fun <T : Any> property(defaultValue: T, init: Property.Builder<T>.() -> Unit = {}) =
|
||||
propertyDelegate(context.propertyContext, init, defaultValue)
|
||||
|
||||
fun <T : Any> listProperty(vararg defaultValues: T, init: Property.Builder<List<T>>.() -> Unit = {}) =
|
||||
property(defaultValues.toList(), init)
|
||||
|
||||
|
||||
// setting types
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
final override fun <V : DisplayableSettingItem> dropDownSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: DropDownSettingType.Builder<V>.() -> Unit
|
||||
): ReadOnlyProperty<Any, PluginSetting<V, DropDownSettingType<V>>> =
|
||||
super.dropDownSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
parser,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, PluginSetting<V, DropDownSettingType<V>>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
final override fun stringSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: StringSettingType.Builder.() -> Unit
|
||||
): ReadOnlyProperty<Any, PluginSetting<String, StringSettingType>> =
|
||||
super.stringSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, PluginSetting<String, StringSettingType>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
final override fun booleanSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: BooleanSettingType.Builder.() -> Unit
|
||||
): ReadOnlyProperty<Any, PluginSetting<Boolean, BooleanSettingType>> =
|
||||
super.booleanSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, PluginSetting<Boolean, BooleanSettingType>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
final override fun <V : Any> valueSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: ValueSettingType.Builder<V>.() -> Unit
|
||||
): ReadOnlyProperty<Any, PluginSetting<V, ValueSettingType<V>>> =
|
||||
super.valueSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
parser,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, PluginSetting<V, ValueSettingType<V>>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
final override fun versionSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: VersionSettingType.Builder.() -> Unit
|
||||
): ReadOnlyProperty<Any, PluginSetting<Version, VersionSettingType>> =
|
||||
super.versionSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, PluginSetting<Version, VersionSettingType>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
final override fun <V : Any> listSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: ListSettingType.Builder<V>.() -> Unit
|
||||
): ReadOnlyProperty<Any, PluginSetting<List<V>, ListSettingType<V>>> =
|
||||
super.listSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
parser,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, PluginSetting<List<V>, ListSettingType<V>>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
final override fun pathSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: PathSettingType.Builder.() -> Unit
|
||||
): ReadOnlyProperty<Any, PluginSetting<Path, PathSettingType>> =
|
||||
super.pathSetting(title, neededAtPhase, init) as ReadOnlyProperty<Any, PluginSetting<Path, PathSettingType>>
|
||||
|
||||
inline fun <reified E> enumSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
crossinline init: DropDownSettingType.Builder<E>.() -> Unit = {}
|
||||
) where E : Enum<E>, E : DisplayableSettingItem = dropDownSetting<E>(title, neededAtPhase, enumParser()) {
|
||||
values = enumValues<E>().asList()
|
||||
init()
|
||||
}
|
||||
}
|
||||
|
||||
val PluginReference.withParentPlugins
|
||||
get() = generateSequence(this) { klass ->
|
||||
klass.supertypes.firstOrNull { supertype ->
|
||||
supertype.classifier?.safeAs<KClass<Plugin>>()
|
||||
?.isSubclassOf(Plugin::class) == true
|
||||
}?.classifier
|
||||
?.safeAs<KClass<Plugin>>()
|
||||
?.takeIf { superClass ->
|
||||
superClass.simpleName != null
|
||||
}
|
||||
}
|
||||
|
||||
val PluginReference.name
|
||||
get() = simpleName
|
||||
?.removeSuffix("Plugin")
|
||||
?.decapitalize()
|
||||
.orEmpty()
|
||||
|
||||
val PluginReference.path
|
||||
get() = withParentPlugins.mapNotNull { klass ->
|
||||
klass.name.takeIf { it.isNotEmpty() }
|
||||
}.toList()
|
||||
.reversed()
|
||||
.joinToString(".")
|
||||
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
|
||||
import kotlin.coroutines.intrinsics.createCoroutineUnintercepted
|
||||
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
|
||||
sealed class TaskResult<out T : Any>
|
||||
data class Success<T : Any>(val value: T) : TaskResult<T>()
|
||||
data class Failure(val errors: List<Error>) : TaskResult<Nothing>() {
|
||||
constructor(vararg errors: Error) : this(errors.toList())
|
||||
}
|
||||
|
||||
val TaskResult<Any>.isSuccess
|
||||
get() = this is Success<*>
|
||||
|
||||
fun <T : Any> success(value: T): TaskResult<T> =
|
||||
Success(value)
|
||||
|
||||
fun <T : Any> failure(vararg errors: Error): TaskResult<T> =
|
||||
Failure(*errors)
|
||||
|
||||
val <T : Any> TaskResult<T>.asNullable: T?
|
||||
get() = safeAs<Success<T>>()?.value
|
||||
|
||||
inline fun <T : Any> TaskResult<T>.onSuccess(action: (T) -> Unit) = also {
|
||||
if (this is Success<T>) {
|
||||
action(value)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T : Any> TaskResult<T>.onFailure(handler: (List<Error>) -> Unit) = apply {
|
||||
if (this is Failure) {
|
||||
handler(errors)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <A : Any, B : Any, R : Any> TaskResult<A>.mappend(
|
||||
other: TaskResult<B>,
|
||||
op: (A, B) -> R
|
||||
): TaskResult<R> = mappendM(other) { a, b -> success(op(a, b)) }
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <A : Any, B : Any, R : Any> TaskResult<A>.mappendM(
|
||||
other: TaskResult<B>,
|
||||
op: (A, B) -> TaskResult<R>
|
||||
): TaskResult<R> = when {
|
||||
this is Success<*> && other is Success<*> -> op(value as A, other.value as B)
|
||||
this is Success<*> && other is Failure -> other
|
||||
this is Failure && other is Success<*> -> this
|
||||
this is Failure && other is Failure -> Failure(errors + other.errors)
|
||||
else -> error("Can not happen")
|
||||
}
|
||||
|
||||
infix fun <T : Any> TaskResult<*>.andThen(other: TaskResult<T>): TaskResult<T> =
|
||||
mappendM(other) { _, _ -> other }
|
||||
|
||||
operator fun <T : Any> TaskResult<List<T>>.plus(other: TaskResult<List<T>>): TaskResult<Unit> =
|
||||
mappend(other) { _, _ -> Unit }
|
||||
|
||||
|
||||
fun <T : Any> T?.toResult(failure: () -> Error): TaskResult<T> =
|
||||
this?.let { Success(it) } ?: Failure(
|
||||
failure()
|
||||
)
|
||||
|
||||
fun <T : Any> T.asSuccess(): Success<T> =
|
||||
Success(this)
|
||||
|
||||
inline fun <T : Any> TaskResult<T>.raise(returnExpression: (Failure) -> (Unit)): T =
|
||||
when (this) {
|
||||
is Failure -> {
|
||||
returnExpression(this)
|
||||
error("fail should return out of the function")
|
||||
}
|
||||
is Success -> value
|
||||
}
|
||||
|
||||
|
||||
fun <T : Any> Iterable<TaskResult<T>>.sequence(): TaskResult<List<T>> =
|
||||
fold(success(emptyList())) { acc, result ->
|
||||
acc.mappend(result) { a, r -> a + r }
|
||||
}
|
||||
|
||||
fun <T : Any> Iterable<TaskResult<T>>.sequenceIgnore(): TaskResult<Unit> =
|
||||
fold<TaskResult<T>, TaskResult<Unit>>(UNIT_SUCCESS) { acc, result ->
|
||||
acc.mappend(result) { _, _ -> Unit }
|
||||
}
|
||||
|
||||
fun <T : Any> Iterable<T>.mapSequenceIgnore(f: (T) -> TaskResult<*>): TaskResult<Unit> =
|
||||
fold<T, TaskResult<Unit>>(UNIT_SUCCESS) { acc, result ->
|
||||
acc.mappend(f(result)) { _, _ -> Unit }
|
||||
}
|
||||
|
||||
fun <T : Any, R : Any> Iterable<T>.mapSequence(f: (T) -> TaskResult<R>): TaskResult<List<R>> =
|
||||
map(f).sequence()
|
||||
|
||||
fun <T : Any> Sequence<TaskResult<T>>.sequenceFailFirst(): TaskResult<List<T>> =
|
||||
fold(success(emptyList())) { acc, result ->
|
||||
if (acc.isSuccess) acc.mappend(result) { a, r -> a + r }
|
||||
else acc
|
||||
}
|
||||
|
||||
fun <T : Any, R : Any> TaskResult<T>.map(f: (T) -> R): TaskResult<R> = when (this) {
|
||||
is Failure -> this
|
||||
is Success<T> -> Success(f(value))
|
||||
}
|
||||
|
||||
fun <T : Any> TaskResult<T>.mapFailure(f: (List<Error>) -> List<Error>): TaskResult<T> = when (this) {
|
||||
is Failure -> Failure(f(errors))
|
||||
is Success<T> -> this
|
||||
}
|
||||
|
||||
fun <T : Any> TaskResult<T>.recover(f: (Failure) -> TaskResult<T>): TaskResult<T> = when (this) {
|
||||
is Failure -> f(this)
|
||||
is Success<T> -> this
|
||||
}
|
||||
|
||||
fun <T : Any, R : Any> TaskResult<T>.flatMap(f: (T) -> TaskResult<R>): TaskResult<R> = when (this) {
|
||||
is Failure -> this
|
||||
is Success<T> -> f(value)
|
||||
}
|
||||
|
||||
fun <T : Any> TaskResult<T>.withAssert(assertion: (T) -> Error?): TaskResult<T> = when (this) {
|
||||
is Failure -> this
|
||||
is Success<T> -> assertion(value)?.let { Failure(it) } ?: this
|
||||
}
|
||||
|
||||
|
||||
fun <T : Any> TaskResult<T>.getOrElse(default: () -> T): T = when (this) {
|
||||
is Success<T> -> value
|
||||
is Failure -> default()
|
||||
}
|
||||
|
||||
fun <T : Any> TaskResult<T>.ignore(): TaskResult<Unit> = when (this) {
|
||||
is Failure -> this
|
||||
is Success<T> -> UNIT_SUCCESS
|
||||
}
|
||||
|
||||
val UNIT_SUCCESS = Success(Unit)
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.PropertyReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.Task1
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.Task1Reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.Service
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
class TaskRunningContext(context: Context, services: List<Service>) : ValuesReadingContext(context, services) {
|
||||
fun <A, B : Any> Task1Reference<A, B>.execute(value: A): TaskResult<B> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val task = context.taskContext.getEntity(this) as Task1<A, B>
|
||||
return task.action(this@TaskRunningContext, value)
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> PropertyReference<T>.update(
|
||||
crossinline updater: suspend ComputeContext<*>.(T) -> TaskResult<T>
|
||||
): TaskResult<Unit> = compute {
|
||||
val (newValue) = updater(propertyValue)
|
||||
context.propertyContext[this@update] = newValue
|
||||
Unit
|
||||
}
|
||||
|
||||
fun <T : Any> PropertyReference<List<T>>.addValues(
|
||||
vararg values: T
|
||||
): TaskResult<Unit> = update { oldValues -> success(oldValues + values) }
|
||||
|
||||
fun <T : Any> PropertyReference<List<T>>.addValues(
|
||||
values: List<T>
|
||||
): TaskResult<Unit> = update { oldValues -> success(oldValues + values) }
|
||||
}
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.PipelineTask
|
||||
|
||||
class TaskSorter {
|
||||
private enum class State {
|
||||
VISITED, NOT_VISITED, ADDED
|
||||
}
|
||||
|
||||
fun sort(
|
||||
tasks: List<PipelineTask>,
|
||||
taskToDependencies: Map<PipelineTask, List<PipelineTask>>
|
||||
): TaskResult<List<PipelineTask>> {
|
||||
val states = Array(tasks.size) { State.NOT_VISITED }
|
||||
val result = mutableListOf<PipelineTask>()
|
||||
val taskToIndex = tasks.indices.associateBy { tasks[it] }
|
||||
fun dfs(index: Int): TaskResult<Unit> {
|
||||
when (states[index]) {
|
||||
State.ADDED -> return UNIT_SUCCESS
|
||||
State.VISITED -> return Failure(
|
||||
CircularTaskDependencyError(tasks[index].path)
|
||||
)
|
||||
State.NOT_VISITED -> {
|
||||
states[index] = State.VISITED
|
||||
}
|
||||
}
|
||||
taskToDependencies[tasks[index]].orEmpty().mapNotNull { dependent ->
|
||||
dfs(taskToIndex[dependent] ?: return@mapNotNull null)
|
||||
}.sequence().raise { return it }
|
||||
|
||||
result += tasks[index]
|
||||
states[index] = State.ADDED
|
||||
return UNIT_SUCCESS
|
||||
}
|
||||
|
||||
return tasks.indices.map { index ->
|
||||
dfs(index)
|
||||
}.sequence().map { result }
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.Service
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
import kotlin.reflect.jvm.jvmName
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
open class ValuesReadingContext(val context: Context, private val services: List<Service>) {
|
||||
inline fun <reified S : Service> service() = serviceByClass(S::class)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <S : Service> serviceByClass(klass: KClass<S>) =
|
||||
services.firstOrNull { it::class.isSubclassOf(klass) } as? S ?: error("No service ${klass.jvmName}")
|
||||
|
||||
inline val <reified T : Any> PropertyReference<T>.propertyValue: T
|
||||
get() = context.propertyContext[this] as T
|
||||
|
||||
inline val <reified V : Any, T : SettingType<V>> SettingReference<V, T>.settingValue: V
|
||||
get() = context.settingContext[this] ?: error("No value is present for setting `$this`")
|
||||
|
||||
inline val <reified V : Any> KProperty1<out Plugin, PluginSetting<V, SettingType<V>>>.settingValue: V
|
||||
get() = reference.settingValue
|
||||
|
||||
inline fun <reified V : Any> KProperty1<out Plugin, PluginSetting<V, SettingType<V>>>.settingValue(): V =
|
||||
this.reference.settingValue
|
||||
|
||||
inline fun <reified V : Any, T : SettingType<V>> SettingReference<V, T>.settingValue(): V =
|
||||
context.settingContext[this] ?: error("No value is present for setting `$this`")
|
||||
|
||||
val <V : Any, T : SettingType<V>> SettingReference<V, T>.notRequiredSettingValue: V?
|
||||
get() = context.settingContext[this]
|
||||
|
||||
fun <V : Any, T : SettingType<V>> SettingReference<V, T>.notRequiredSettingValue(): V? =
|
||||
context.settingContext[this]
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
|
||||
import kotlin.coroutines.intrinsics.createCoroutineUnintercepted
|
||||
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
interface ComputeContextState
|
||||
|
||||
object NoState : ComputeContextState
|
||||
|
||||
data class StateContainer<S : ComputeContextState>(var state: S)
|
||||
|
||||
open class ComputeContext<S : ComputeContextState> protected constructor(private val stateContainer: StateContainer<S>) {
|
||||
private var result: TaskResult<Any>? = null
|
||||
private var exception: Throwable? = null
|
||||
|
||||
val state
|
||||
get() = stateContainer.state
|
||||
|
||||
suspend fun <T : Any> TaskResult<T>.get(): T = suspendCoroutineUninterceptedOrReturn { continuation ->
|
||||
result = this
|
||||
if (this is Success<T>) {
|
||||
continuation.resume(value)
|
||||
}
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
suspend operator fun <T : Any> TaskResult<T>.component1(): T = get()
|
||||
|
||||
suspend fun <T : Any> TaskResult<T>.ensure() {
|
||||
get()
|
||||
}
|
||||
|
||||
suspend fun fail(error: Error): Nothing = suspendCoroutineUninterceptedOrReturn {
|
||||
result = Failure(error)
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun fail(errors: List<Error>): Nothing = suspendCoroutineUninterceptedOrReturn {
|
||||
result = Failure(errors)
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
suspend inline fun <T : Any> TaskResult<T>?.nullableValue(): T? =
|
||||
this?.onFailure { fail(it) }?.asNullable
|
||||
|
||||
fun <T : Any> compute(
|
||||
block: suspend ComputeContext<S>.() -> T
|
||||
): TaskResult<T> = computeM { success(block()) }
|
||||
|
||||
fun <T : Any> computeM(
|
||||
block: suspend ComputeContext<S>.() -> TaskResult<T>
|
||||
): TaskResult<T> {
|
||||
val computeContext = ComputeContext(stateContainer)
|
||||
val continuation = object : Continuation<TaskResult<T>> {
|
||||
override val context: CoroutineContext
|
||||
get() = EmptyCoroutineContext
|
||||
|
||||
override fun resumeWith(result: Result<TaskResult<T>>) {
|
||||
result.onSuccess { computeContext.result = it }
|
||||
result.onFailure { computeContext.exception = it }
|
||||
}
|
||||
}
|
||||
block.createCoroutineUnintercepted(computeContext, continuation).resume(Unit)
|
||||
if (computeContext.exception != null) {
|
||||
throw computeContext.exception!!
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST") return computeContext.result as TaskResult<T>
|
||||
}
|
||||
|
||||
fun <A, B : Any> Iterable<A>.mapCompute(
|
||||
f: suspend ComputeContext<S>.(A) -> B
|
||||
): List<TaskResult<B>> = map { compute { f(it) } }
|
||||
|
||||
fun <A, B : Any> Iterable<A>.mapComputeM(
|
||||
f: suspend ComputeContext<S>.(A) -> TaskResult<B>
|
||||
): List<TaskResult<B>> = map { computeM { f(it) } }
|
||||
|
||||
|
||||
fun updateState(updater: (S) -> S) {
|
||||
stateContainer.state = updater(stateContainer.state)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val PURE = ComputeContext(StateContainer(NoState))
|
||||
|
||||
fun <S : ComputeContextState, R : Any> runInComputeContextWithState(
|
||||
state: S,
|
||||
action: ComputeContext<S>.() -> TaskResult<R>
|
||||
): TaskResult<Pair<R, S>> = with(ComputeContext(StateContainer(state))) {
|
||||
val result = action()
|
||||
result.map { it to this.state }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun <T : Any> compute(
|
||||
block: suspend ComputeContext<NoState>.() -> T
|
||||
): TaskResult<T> = ComputeContext.PURE.compute(block)
|
||||
|
||||
|
||||
fun <T : Any> computeM(
|
||||
block: suspend ComputeContext<NoState>.() -> TaskResult<T>
|
||||
): TaskResult<T> = ComputeContext.PURE.computeM(block)
|
||||
|
||||
|
||||
fun <A, B : Any> Iterable<A>.mapCompute(
|
||||
f: suspend ComputeContext<NoState>.(A) -> B
|
||||
): List<TaskResult<B>> = with(ComputeContext.PURE) {
|
||||
mapCompute(f)
|
||||
}
|
||||
|
||||
fun <A, B : Any> Iterable<A>.mapComputeM(
|
||||
f: suspend ComputeContext<NoState>.(A) -> TaskResult<B>
|
||||
): List<TaskResult<B>> = with(ComputeContext.PURE) {
|
||||
mapComputeM(f)
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.entity
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.SettingsOwner
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Plugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.PluginReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.path
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.safeAs
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.full.declaredMemberProperties
|
||||
import kotlin.reflect.jvm.javaGetter
|
||||
|
||||
interface Entity {
|
||||
val path: String
|
||||
}
|
||||
abstract class EntityBase: Entity {
|
||||
final override fun equals(other: Any?): Boolean = other.safeAs<Entity>()?.path == path
|
||||
final override fun hashCode(): Int = path.hashCode()
|
||||
final override fun toString(): String = path
|
||||
}
|
||||
|
||||
abstract class EntityWithValue<out T : Any> : EntityBase()
|
||||
|
||||
typealias EntityReference = KProperty1<out SettingsOwner, Entity>
|
||||
|
||||
val EntityReference.path
|
||||
get() = "${plugin.path}.$name"
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val <EP : EntityReference> EP.original
|
||||
get() = plugin.declaredMemberProperties.first { it.name == name } as EP
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val EntityReference.plugin: PluginReference
|
||||
get() = javaGetter!!.declaringClass.kotlin as PluginReference
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.entity
|
||||
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
abstract class EntityContext<E : Entity, ER : EntityReference> {
|
||||
private val entities = mutableMapOf<ER, E>()
|
||||
|
||||
open fun addEntity(entityReference: ER, createEntityByName: (String) -> E): E =
|
||||
createEntityByName(entityReference.original.path).also {
|
||||
entities[entityReference.original] = it
|
||||
}
|
||||
|
||||
fun getEntity(entityReference: ER): E? = entities[entityReference.original]
|
||||
fun getAll(): List<E> = entities.values.toList()
|
||||
}
|
||||
|
||||
abstract class ValuedEntityContext<E : EntityWithValue<Any>, ER : EntityReference> : EntityContext<E, ER>() {
|
||||
private val values = mutableMapOf<String, Any>()
|
||||
|
||||
operator fun get(entityReference: ER) =
|
||||
values[entityReference.original.path]
|
||||
|
||||
operator fun get(entityPath: String) =
|
||||
values[entityPath]
|
||||
|
||||
open operator fun set(entityReference: ER, value: Any) {
|
||||
values[entityReference.original.path] = value
|
||||
}
|
||||
|
||||
open operator fun set(entityPath: String, value: Any) {
|
||||
values[entityPath] = value
|
||||
}
|
||||
|
||||
val allValues: Map<String, Any> = values
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <E : Entity, A : E, ER : EntityReference> entityDelegate(
|
||||
entityContext: EntityContext<E, ER>,
|
||||
createEntityByPath: (String) -> A
|
||||
) = object : ReadOnlyProperty<Any, A> {
|
||||
override fun getValue(thisRef: Any, property: KProperty<*>): A =
|
||||
entityContext.getEntity(property as ER) as? A
|
||||
?: entityContext.addEntity(property, createEntityByPath) as A
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.entity
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Plugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.PluginReference
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
typealias PropertyReference<T> = KProperty1<out Plugin, Property<T>>
|
||||
|
||||
class PropertyContext : ValuedEntityContext<Property<Any>, PropertyReference<Any>>()
|
||||
|
||||
|
||||
data class Property<out T : Any>(
|
||||
override val path: String,
|
||||
val defaultValue: T
|
||||
) : EntityWithValue<T>() {
|
||||
class Builder<T : Any>(
|
||||
private val name: String,
|
||||
private val defaultValue: T
|
||||
) {
|
||||
fun build(): Property<T> = Property(name, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun <T : Any> propertyDelegate(
|
||||
context: PropertyContext,
|
||||
init: Property.Builder<T>.() -> Unit,
|
||||
defaultValue: T
|
||||
) = entityDelegate(context) { name ->
|
||||
Property.Builder(name, defaultValue).apply(init).build()
|
||||
}
|
||||
+385
@@ -0,0 +1,385 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.entity
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.Identificator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.id
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.ModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||
import org.jetbrains.kotlin.tools.projectWizard.templates.Template
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
sealed class SettingReference<out V : Any, out T : SettingType<V>> {
|
||||
abstract val path: String
|
||||
abstract val type: KClass<out T>
|
||||
|
||||
abstract fun Context.getSetting(): Setting<V, T>
|
||||
|
||||
final override fun toString() = path
|
||||
final override fun equals(other: Any?) = other.safeAs<SettingReference<*, *>>()?.path == path
|
||||
final override fun hashCode() = path.hashCode()
|
||||
}
|
||||
|
||||
data class PluginSettingReference<V : Any, T : SettingType<V>>(
|
||||
override val path: String,
|
||||
override val type: KClass<T>
|
||||
) : SettingReference<V, T>() {
|
||||
|
||||
constructor(kProperty: KProperty1<out Plugin, PluginSetting<V, T>>, type: KClass<T>) :
|
||||
this(kProperty.path, type)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
constructor(setting: PluginSetting<V, T>) :
|
||||
this(setting.path, setting.type::class as KClass<T>)
|
||||
|
||||
override fun Context.getSetting(): Setting<V, T> =
|
||||
settingContext.getPluginSetting(this@PluginSettingReference)
|
||||
}
|
||||
|
||||
data class ModuleConfiguratorSettingReference<V : Any, T : SettingType<V>>(
|
||||
val descriptor: ModuleConfigurator,
|
||||
val moduleId: Identificator,
|
||||
val setting: ModuleConfiguratorSetting<V, T>
|
||||
) : SettingReference<V, T>() {
|
||||
constructor(descriptor: ModuleConfigurator, module: Module, setting: ModuleConfiguratorSetting<V, T>) :
|
||||
this(descriptor, module.identificator, setting)
|
||||
|
||||
override val path: String
|
||||
get() = "${descriptor.id}/$moduleId/${setting.path}"
|
||||
|
||||
override val type: KClass<out T>
|
||||
get() = setting.type::class
|
||||
|
||||
override fun Context.getSetting(): Setting<V, T> = setting
|
||||
}
|
||||
|
||||
sealed class TemplateSettingReference<V : Any, T : SettingType<V>> : SettingReference<V, T>() {
|
||||
abstract val descriptor: Template
|
||||
abstract val setting: TemplateSetting<V, T>
|
||||
abstract val sourcesetId: Identificator
|
||||
|
||||
override val path: String
|
||||
get() = "${descriptor.id}/$sourcesetId/${setting.path}"
|
||||
|
||||
override val type: KClass<out T>
|
||||
get() = setting.type::class
|
||||
|
||||
override fun Context.getSetting(): Setting<V, T> = setting
|
||||
abstract val sourceset: Sourceset?
|
||||
}
|
||||
|
||||
data class SourcesetBasedTemplateSettingReference<V : Any, T : SettingType<V>>(
|
||||
override val descriptor: Template,
|
||||
override val sourceset: Sourceset,
|
||||
override val setting: TemplateSetting<V, T>
|
||||
) : TemplateSettingReference<V, T>() {
|
||||
override val sourcesetId: Identificator
|
||||
get() = sourceset.identificator
|
||||
}
|
||||
|
||||
data class IdBasedTemplateSettingReference<V : Any, T : SettingType<V>>(
|
||||
override val descriptor: Template,
|
||||
override val sourcesetId: Identificator,
|
||||
override val setting: TemplateSetting<V, T>
|
||||
) : TemplateSettingReference<V, T>() {
|
||||
override val sourceset: Sourceset? = null
|
||||
}
|
||||
|
||||
inline val <V : Any, reified T : SettingType<V>> PluginSettingPropertyReference<V, T>.reference: PluginSettingReference<V, T>
|
||||
get() = PluginSettingReference(this, T::class)
|
||||
|
||||
typealias PluginSettingPropertyReference<V, T> = KProperty1<out Plugin, PluginSetting<V, T>>
|
||||
typealias SettingPropertyReference<V, T> = KProperty1<out Plugin, Setting<V, T>>
|
||||
|
||||
class SettingContext(val onUpdated: (SettingReference<*, *>) -> Unit) {
|
||||
private val values = mutableMapOf<String, Any>()
|
||||
private val pluginSettings = mutableMapOf<String, PluginSetting<*, *>>()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
operator fun <V : Any, T : SettingType<V>> get(
|
||||
reference: SettingReference<V, T>
|
||||
): V? = values[reference.path] as? V
|
||||
|
||||
operator fun <V : Any, T : SettingType<V>> set(
|
||||
reference: SettingReference<V, T>,
|
||||
newValue: V
|
||||
) {
|
||||
values[reference.path] = newValue
|
||||
onUpdated(reference)
|
||||
}
|
||||
|
||||
fun initPluginSettings(settings: List<PluginSetting<*, *>>) {
|
||||
for (setting in settings) {
|
||||
setting.defaultValue?.let { values[setting.path] = it }
|
||||
}
|
||||
}
|
||||
|
||||
val allPluginSettings: Collection<PluginSetting<*, *>>
|
||||
get() = pluginSettings.values
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <V : Any, T : SettingType<V>> getPluginSetting(pluginSettingReference: PluginSettingReference<V, T>) =
|
||||
pluginSettings[pluginSettingReference.path] as PluginSetting<V, T>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <V : Any, T : SettingType<V>> getPluginSetting(pluginSettingReference: PluginSettingPropertyReference<V, T>) =
|
||||
pluginSettings[pluginSettingReference.path] as? PluginSetting<V, T>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <V : Any, T : SettingType<V>> setPluginSetting(
|
||||
pluginSettingReference: PluginSettingPropertyReference<V, T>,
|
||||
setting: PluginSetting<V, T>
|
||||
) {
|
||||
pluginSettings[pluginSettingReference.path] = setting
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <V : Any, T : SettingType<V>> pluginSettingValue(setting: PluginSetting<V, T>): V? =
|
||||
values[setting.path] as? V
|
||||
}
|
||||
|
||||
|
||||
typealias AnySetting = Setting<*, *>
|
||||
|
||||
interface Setting<out V : Any, out T : SettingType<V>> : Entity, ActivityCheckerOwner, Validatable<V> {
|
||||
val title: String
|
||||
val defaultValue: V?
|
||||
val isRequired: Boolean
|
||||
var neededAtPhase: GenerationPhase
|
||||
val type: T
|
||||
|
||||
}
|
||||
|
||||
data class InternalSetting<out V : Any, out T : SettingType<V>>(
|
||||
override val path: String,
|
||||
override val title: String,
|
||||
override val defaultValue: V?,
|
||||
override val activityChecker: Checker,
|
||||
override val isRequired: Boolean,
|
||||
override var neededAtPhase: GenerationPhase,
|
||||
override val validator: SettingValidator<@UnsafeVariance V>,
|
||||
override val type: T
|
||||
) : Setting<V, T>, EntityWithValue<V>()
|
||||
|
||||
sealed class SettingImpl<out V : Any, out T : SettingType<V>> : Setting<V, T>
|
||||
|
||||
class PluginSetting<out V : Any, out T : SettingType<V>>(
|
||||
internal: InternalSetting<V, T>
|
||||
) : SettingImpl<V, T>(), Setting<V, T> by internal
|
||||
|
||||
class ModuleConfiguratorSetting<out V : Any, out T : SettingType<V>>(
|
||||
internal: InternalSetting<V, T>
|
||||
) : SettingImpl<V, T>(), Setting<V, T> by internal
|
||||
|
||||
class TemplateSetting<out V : Any, out T : SettingType<V>>(
|
||||
internal: InternalSetting<V, T>
|
||||
) : SettingImpl<V, T>(), Setting<V, T> by internal
|
||||
|
||||
|
||||
abstract class SettingBuilder<V : Any, T : SettingType<V>>(
|
||||
private val path: String,
|
||||
private val title: String,
|
||||
private val neededAtPhase: GenerationPhase
|
||||
) {
|
||||
var checker: Checker = Checker.ALWAYS_AVAILABLE
|
||||
var defaultValue: V? = null
|
||||
|
||||
protected var validator = SettingValidator<V> { ValidationResult.OK }
|
||||
|
||||
fun validate(validator: SettingValidator<V>) {
|
||||
this.validator = this.validator and validator
|
||||
}
|
||||
|
||||
fun validate(validator: ValuesReadingContext.(V) -> ValidationResult) {
|
||||
this.validator = this.validator and settingValidator(validator)
|
||||
}
|
||||
|
||||
|
||||
abstract val type: T
|
||||
|
||||
fun buildInternal() = InternalSetting(
|
||||
path = path,
|
||||
title = title,
|
||||
defaultValue = defaultValue,
|
||||
activityChecker = checker,
|
||||
isRequired = defaultValue == null,
|
||||
neededAtPhase = neededAtPhase,
|
||||
validator = validator,
|
||||
type = type
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
sealed class SettingType<out V : Any> {
|
||||
abstract fun parse(context: ParsingContext, value: Any, name: String): TaskResult<V>
|
||||
}
|
||||
|
||||
object StringSettingType : SettingType<String>() {
|
||||
override fun parse(context: ParsingContext, value: Any, name: String) =
|
||||
value.parseAs<String>(name)
|
||||
|
||||
class Builder(
|
||||
path: String,
|
||||
private val title: String,
|
||||
neededAtPhase: GenerationPhase
|
||||
) : SettingBuilder<String, StringSettingType>(path, title, neededAtPhase) {
|
||||
fun shouldNotBeBlank() = validate { value: String ->
|
||||
if (value.isBlank()) ValidationResult.ValidationError("`${title.capitalize()}` should not be blank ")
|
||||
else ValidationResult.OK
|
||||
}
|
||||
|
||||
override val type = StringSettingType
|
||||
}
|
||||
}
|
||||
|
||||
object BooleanSettingType : SettingType<Boolean>() {
|
||||
override fun parse(context: ParsingContext, value: Any, name: String) =
|
||||
value.parseAs<Boolean>(name)
|
||||
|
||||
class Builder(
|
||||
path: String,
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase
|
||||
) : SettingBuilder<Boolean, BooleanSettingType>(path, title, neededAtPhase) {
|
||||
override val type = BooleanSettingType
|
||||
}
|
||||
}
|
||||
|
||||
class DropDownSettingType<V : DisplayableSettingItem>(
|
||||
val values: List<V>,
|
||||
val filter: DropDownSettingTypeFilter<V>,
|
||||
val parser: Parser<V>
|
||||
) : SettingType<V>() {
|
||||
override fun parse(context: ParsingContext, value: Any, name: String): TaskResult<V> = with(context) {
|
||||
computeM {
|
||||
parser.parse(this, value, name)
|
||||
}
|
||||
}
|
||||
|
||||
class Builder<V : DisplayableSettingItem>(
|
||||
path: String,
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
private val parser: Parser<V>
|
||||
) : SettingBuilder<V, DropDownSettingType<V>>(path, title, neededAtPhase) {
|
||||
var values = emptyList<V>()
|
||||
|
||||
var filter: DropDownSettingTypeFilter<V> = { _, _ -> true }
|
||||
|
||||
override val type
|
||||
get() = DropDownSettingType(values, filter, parser)
|
||||
}
|
||||
}
|
||||
|
||||
typealias DropDownSettingTypeFilter<V> = (SettingReference<V, DropDownSettingType<V>>, V) -> Boolean
|
||||
|
||||
|
||||
class ValueSettingType<V : Any>(
|
||||
private val parser: Parser<V>
|
||||
) : SettingType<V>() {
|
||||
override fun parse(context: ParsingContext, value: Any, name: String): TaskResult<V> = with(context) {
|
||||
computeM {
|
||||
parser.parse(this, value, name)
|
||||
}
|
||||
}
|
||||
|
||||
class Builder<V : Any>(
|
||||
private val path: String,
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
private val parser: Parser<V>
|
||||
) : SettingBuilder<V, ValueSettingType<V>>(path, title, neededAtPhase) {
|
||||
init {
|
||||
validate { value ->
|
||||
if (value is Validatable<*>) value.validator.validate(this, value)
|
||||
else ValidationResult.OK
|
||||
}
|
||||
}
|
||||
|
||||
override val type
|
||||
get() = ValueSettingType(parser)
|
||||
}
|
||||
}
|
||||
|
||||
object VersionSettingType : SettingType<Version>() {
|
||||
override fun parse(context: ParsingContext, value: Any, name: String): TaskResult<Version> = with(context) {
|
||||
computeM {
|
||||
Version.parser.parse(this, value, name)
|
||||
}
|
||||
}
|
||||
|
||||
class Builder(
|
||||
path: String,
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase
|
||||
) : SettingBuilder<Version, VersionSettingType>(path, title, neededAtPhase) {
|
||||
override val type
|
||||
get() = VersionSettingType
|
||||
}
|
||||
}
|
||||
|
||||
class ListSettingType<V : Any>(private val parser: Parser<V>) : SettingType<List<V>>() {
|
||||
override fun parse(context: ParsingContext, value: Any, name: String): TaskResult<List<V>> = with(context) {
|
||||
computeM {
|
||||
val (list) = value.parseAs<List<*>>(name)
|
||||
list.mapComputeM { parser.parse(this, it, name) }.sequence()
|
||||
}
|
||||
}
|
||||
|
||||
class Builder<V : Any>(
|
||||
path: String,
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>
|
||||
) : SettingBuilder<List<V>, ListSettingType<V>>(path, title, neededAtPhase) {
|
||||
init {
|
||||
validate { values ->
|
||||
values.fold(ValidationResult.OK as ValidationResult) { result, value ->
|
||||
result and when (value) {
|
||||
is Validatable<*> -> value.validator.validate(this, value)
|
||||
else -> ValidationResult.OK
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val type = ListSettingType(parser)
|
||||
}
|
||||
}
|
||||
|
||||
object PathSettingType : SettingType<Path>() {
|
||||
override fun parse(context: ParsingContext, value: Any, name: String): TaskResult<Path> = with(context) {
|
||||
computeM {
|
||||
pathParser.parse(this, value, name)
|
||||
}
|
||||
}
|
||||
|
||||
class Builder(
|
||||
path: String,
|
||||
private val title: String,
|
||||
neededAtPhase: GenerationPhase
|
||||
) : SettingBuilder<Path, PathSettingType>(path, title, neededAtPhase) {
|
||||
|
||||
init {
|
||||
validate { pathValue ->
|
||||
if (pathValue.toString().isBlank())
|
||||
ValidationResult.ValidationError("${title.capitalize()} should not be blank")
|
||||
else ValidationResult.OK
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldExists() = validate { pathValue ->
|
||||
if (!Files.exists(pathValue))
|
||||
ValidationResult.ValidationError("File for ${title.capitalize()} should exists")
|
||||
else ValidationResult.OK
|
||||
}
|
||||
|
||||
override val type = PathSettingType
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.entity
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Failure
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.UNIT_SUCCESS
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValidationError
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
|
||||
inline class SettingValidator<V>(val validate: ValuesReadingContext.(V) -> ValidationResult) {
|
||||
infix fun and(other: SettingValidator<V>) = SettingValidator<V> { value ->
|
||||
validate(value) and other.validate(this, value)
|
||||
}
|
||||
|
||||
operator fun ValuesReadingContext.invoke(value: V) = validate(value)
|
||||
}
|
||||
|
||||
fun <V> settingValidator(validator: ValuesReadingContext.(V) -> ValidationResult) =
|
||||
SettingValidator(validator)
|
||||
|
||||
fun <V> inValidatorContext(validator: ValuesReadingContext.(V) -> SettingValidator<V>) =
|
||||
SettingValidator<V> { value ->
|
||||
validator(value).validate(this, value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
typealias StringValidator = SettingValidator<String>
|
||||
|
||||
object StringValidators {
|
||||
fun shouldNotBeBlank(name: String) = settingValidator { value: String ->
|
||||
if (value.isBlank()) ValidationResult.ValidationError("${name.capitalize()} should not be blank ")
|
||||
else ValidationResult.OK
|
||||
}
|
||||
|
||||
fun shouldBeValidIdentifier(name: String) = settingValidator { value: String ->
|
||||
if (value.any { !it.isLetterOrDigit() && it != '_' })
|
||||
ValidationResult.ValidationError(
|
||||
"${name.capitalize()} should consist only of letters, digits, and underscores"
|
||||
)
|
||||
else ValidationResult.OK
|
||||
}
|
||||
}
|
||||
|
||||
fun List<ValidationResult>.fold() = fold(ValidationResult.OK, ValidationResult::and)
|
||||
|
||||
|
||||
sealed class ValidationResult {
|
||||
abstract val isOk: Boolean
|
||||
|
||||
object OK : ValidationResult() {
|
||||
override val isOk = true
|
||||
}
|
||||
|
||||
class ValidationError(val messages: List<String>) : ValidationResult() {
|
||||
constructor(message: String) : this(listOf(message))
|
||||
|
||||
override val isOk = false
|
||||
}
|
||||
|
||||
infix fun and(other: ValidationResult) = when {
|
||||
this is OK -> other
|
||||
this is ValidationError && other is ValidationError -> ValidationError(messages + other.messages)
|
||||
else -> this
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(condition: Boolean, message: String) =
|
||||
if (condition) OK else ValidationError(message)
|
||||
}
|
||||
}
|
||||
|
||||
fun ValidationResult.toResult() = when (this) {
|
||||
ValidationResult.OK -> UNIT_SUCCESS
|
||||
is ValidationResult.ValidationError -> Failure(messages.map { ValidationError(it) })
|
||||
}
|
||||
|
||||
|
||||
interface Validatable<out V> {
|
||||
val validator: SettingValidator<@UnsafeVariance V>
|
||||
}
|
||||
|
||||
fun <V, Q : Validatable<Q>> ValuesReadingContext.validateList(list: List<Q>) = settingValidator<V> {
|
||||
list.fold(ValidationResult.OK as ValidationResult) { result, value ->
|
||||
result and value.validator.validate(this, value)
|
||||
}
|
||||
}
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.entity
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
typealias TaskReference = KProperty1<out Plugin, Task>
|
||||
typealias Task1Reference<A, B> = KProperty1<out Plugin, Task1<A, B>>
|
||||
typealias PipelineTaskReference = KProperty1<out Plugin, PipelineTask>
|
||||
|
||||
class TaskContext : EntityContext<Task, TaskReference>()
|
||||
|
||||
sealed class Task : EntityBase()
|
||||
|
||||
data class Task1<A, B : Any>(
|
||||
override val path: String,
|
||||
val action: TaskRunningContext.(A) -> TaskResult<B>
|
||||
) : Task() {
|
||||
class Builder<A, B : Any>(private val name: String) {
|
||||
private var action: TaskRunningContext.(A) -> TaskResult<B> = { Failure() }
|
||||
|
||||
fun withAction(action: TaskRunningContext.(A) -> TaskResult<B>) {
|
||||
this.action = action
|
||||
}
|
||||
|
||||
fun build(): Task1<A, B> = Task1(name, action)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <A, B : Any> delegate(
|
||||
context: EntityContext<Task, TaskReference>,
|
||||
init: Builder<A, B>.() -> Unit
|
||||
) = entityDelegate(context) { name ->
|
||||
Builder<A, B>(name).apply(init).build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class PipelineTask(
|
||||
override val path: String,
|
||||
val action: TaskRunningContext.() -> TaskResult<Unit>,
|
||||
val before: List<PipelineTaskReference>,
|
||||
val after: List<PipelineTaskReference>,
|
||||
val phase: GenerationPhase,
|
||||
val checker: Checker,
|
||||
val title: String?
|
||||
) : Task() {
|
||||
class Builder(
|
||||
private val name: String,
|
||||
private val phase: GenerationPhase
|
||||
) {
|
||||
private var action: TaskRunningContext.() -> TaskResult<Unit> = { UNIT_SUCCESS }
|
||||
private val before = mutableListOf<PipelineTaskReference>()
|
||||
private val after = mutableListOf<PipelineTaskReference>()
|
||||
|
||||
var activityChecker: Checker = Checker.ALWAYS_AVAILABLE
|
||||
|
||||
var title: String? = null
|
||||
|
||||
fun withAction(action: TaskRunningContext.() -> TaskResult<Unit>) {
|
||||
this.action = action
|
||||
}
|
||||
|
||||
fun runBefore(vararg before: PipelineTaskReference) {
|
||||
this.before.addAll(before)
|
||||
}
|
||||
|
||||
fun runAfter(vararg after: PipelineTaskReference) {
|
||||
this.after.addAll(after)
|
||||
}
|
||||
|
||||
fun build(): PipelineTask = PipelineTask(name, action, before, after, phase, activityChecker, title)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun delegate(
|
||||
context: EntityContext<Task, TaskReference>,
|
||||
phase: GenerationPhase,
|
||||
init: Builder.() -> Unit
|
||||
) = entityDelegate(context) { name ->
|
||||
Builder(name, phase).apply(init).build()
|
||||
}
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.VersionRange
|
||||
import java.io.IOException
|
||||
|
||||
abstract class Error {
|
||||
abstract val message: String
|
||||
}
|
||||
|
||||
abstract class ExceptionError : Error() {
|
||||
abstract val exception: Exception
|
||||
override val message: String
|
||||
get() = exception.asString()
|
||||
}
|
||||
|
||||
data class IOError(override val exception: IOException) : ExceptionError()
|
||||
|
||||
data class ExceptionErrorImpl(override val exception: Exception) : ExceptionError()
|
||||
|
||||
data class ParseError(override val message: String) : Error()
|
||||
|
||||
data class TemplateNotFoundError(val id: String) : Error() {
|
||||
override val message: String
|
||||
get() = "Template with an id `$id` is not found"
|
||||
}
|
||||
|
||||
data class SettingNotFoundError(val settingName: String) : Error() {
|
||||
override val message: String
|
||||
get() = "Setting with name `$settingName` was not found"
|
||||
}
|
||||
|
||||
data class RequiredSettingsIsNotPresentError(val settingNames: List<String>) : Error() {
|
||||
override val message: String
|
||||
get() = buildString {
|
||||
append("The following required settings is not present: \n")
|
||||
settingNames.joinTo(this, "\n") { " $it" }
|
||||
}
|
||||
}
|
||||
|
||||
data class BadApplicationExitCodeError(val applicationName: String, val exitCode: Int) : Error() {
|
||||
override val message: String
|
||||
get() = "Application $applicationName exited with code $exitCode"
|
||||
}
|
||||
|
||||
|
||||
data class CircularTaskDependencyError(val taskName: String) : Error() {
|
||||
override val message: String
|
||||
get() = "$taskName task has circular dependencies"
|
||||
}
|
||||
|
||||
data class BadSettingValueError(override val message: String) : Error()
|
||||
|
||||
data class LibraryNotFoundError(val name: String) : Error() {
|
||||
override val message: String
|
||||
get() = "Library with name `$name` was not found"
|
||||
}
|
||||
|
||||
data class ConfiguratorNotFoundError(val id: String) : Error() {
|
||||
override val message: String
|
||||
get() = "Module type `$id` was not found"
|
||||
}
|
||||
|
||||
data class VersionIsNotInRangeError(val subject: String, val version: Version, val range: VersionRange) : Error() {
|
||||
override val message: String
|
||||
get() = "${subject.capitalize()}'s version $version is not in expected range $range"
|
||||
}
|
||||
|
||||
data class ValidationError(val validationMessage: String) : Error() {
|
||||
override val message: String
|
||||
get() = "Validation error: $validationMessage"
|
||||
}
|
||||
|
||||
data class InvalidSourceSetName(val name: String) : Error() {
|
||||
override val message: String
|
||||
get() = "Source set name `$name` is invalid"
|
||||
}
|
||||
|
||||
data class ModuleNotFoundError(val path: String) : Error() {
|
||||
override val message: String
|
||||
get() = "Sourceset with a path `$path` was not found invalid"
|
||||
}
|
||||
|
||||
|
||||
fun Throwable.asString() =
|
||||
this::class.simpleName!!.removeSuffix("Exception").splitByWords() + message?.let { ": $it" }.orEmpty()
|
||||
|
||||
private val wordRegex = "[A-Z][a-z0-9]+".toRegex()
|
||||
private fun String.splitByWords() =
|
||||
wordRegex.findAll(this).joinToString(separator = " ") { it.value }
|
||||
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.service
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
interface AndroidService : Service {
|
||||
fun isValidAndroidSdk(path: Path): Boolean
|
||||
}
|
||||
|
||||
class AndroidServiceImpl : AndroidService {
|
||||
//TODO use some heuristics
|
||||
override fun isValidAndroidSdk(path: Path): Boolean = true
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.service
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.TaskResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.ModuleIR
|
||||
import java.nio.file.Path
|
||||
|
||||
interface BuildSystemService : Service {
|
||||
fun importProject(
|
||||
path: Path,
|
||||
modulesIrs: List<ModuleIR>
|
||||
): TaskResult<Unit>
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.service
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.TaskResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.UNIT_SUCCESS
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.computeM
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.safe
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
interface FileSystemService : Service {
|
||||
fun createFile(path: Path, text: String): TaskResult<Unit>
|
||||
fun createDirectory(path: Path): TaskResult<Unit>
|
||||
|
||||
object DUMMY : FileSystemService {
|
||||
override fun createFile(path: Path, text: String): TaskResult<Unit> = UNIT_SUCCESS
|
||||
override fun createDirectory(path: Path): TaskResult<Unit> = UNIT_SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
class OsFileSystemService : FileSystemService {
|
||||
override fun createFile(path: Path, text: String) = computeM {
|
||||
createDirectory(path.parent).ensure()
|
||||
safe { Files.createFile(path.normalize()).toFile().writeText(text) }
|
||||
}
|
||||
|
||||
|
||||
override fun createDirectory(path: Path) = safe {
|
||||
@Suppress("NAME_SHADOWING") val path = path.normalize()
|
||||
if (Files.notExists(path)) {
|
||||
Files.createDirectories(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.service
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.UNIT_SUCCESS
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.ModuleIR
|
||||
import java.nio.file.Path
|
||||
|
||||
interface GradleService : BuildSystemService
|
||||
|
||||
class OsGradleIntegration : GradleService {
|
||||
override fun importProject(
|
||||
path: Path,
|
||||
modulesIrs: List<ModuleIR>
|
||||
) = UNIT_SUCCESS
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.service
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.UNIT_SUCCESS
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.ModuleIR
|
||||
import java.nio.file.Path
|
||||
|
||||
interface JpsService : BuildSystemService
|
||||
|
||||
class OsJpsService : JpsService {
|
||||
override fun importProject(
|
||||
path: Path,
|
||||
modulesIrs: List<ModuleIR>
|
||||
) = UNIT_SUCCESS
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.service
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.UNIT_SUCCESS
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.ModuleIR
|
||||
import java.nio.file.Path
|
||||
|
||||
interface MavenService : BuildSystemService
|
||||
|
||||
class OsMavenService : MavenService {
|
||||
override fun importProject(
|
||||
path: Path,
|
||||
modulesIrs: List<ModuleIR>
|
||||
) = UNIT_SUCCESS
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core.service
|
||||
|
||||
interface Service
|
||||
|
||||
object Services {
|
||||
val osServices = listOf(
|
||||
OsGradleIntegration(),
|
||||
OsMavenService(),
|
||||
OsFileSystemService(),
|
||||
OsJpsService(),
|
||||
AndroidServiceImpl()
|
||||
)
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.Setting
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.BuildSystemIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.BodyIR
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.random.Random
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
operator fun Path.div(other: String): Path =
|
||||
resolve(other)
|
||||
|
||||
operator fun Path.div(other: Path): Path =
|
||||
resolve(other)
|
||||
|
||||
operator fun String.div(other: Path): Path =
|
||||
Paths.get(this).resolve(other)
|
||||
|
||||
operator fun String.div(other: String): Path =
|
||||
Paths.get(this).resolve(other)
|
||||
|
||||
fun String.asPath(): Path = Paths.get(this)
|
||||
|
||||
fun <T : Any> safe(operation: () -> T): TaskResult<T> =
|
||||
try {
|
||||
Success(operation())
|
||||
} catch (e: IOException) {
|
||||
Failure(IOError(e))
|
||||
} catch (e: Exception) {
|
||||
Failure(ExceptionErrorImpl(e))
|
||||
}
|
||||
|
||||
internal inline fun <T> cached(crossinline createValue: (name: String) -> T) = object : ReadOnlyProperty<Any?, T> {
|
||||
var value: T? = null
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T =
|
||||
value ?: createValue(property.name).also { value = it }
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal inline fun <reified T> Any?.safeAs(): T? = this as? T
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun Any?.ignore() = Unit
|
||||
|
||||
internal fun <T> T.asSingletonList() = listOf(this)
|
||||
|
||||
inline fun <reified R> Iterable<*>.filterIsInstanceWith(predicate: (R) -> Boolean): List<R> {
|
||||
val result = mutableListOf<R>()
|
||||
for (element in this) {
|
||||
if (element is R && predicate(element)) {
|
||||
result += element
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun Path.asAbsolute(prefix: Path) =
|
||||
if (isAbsolute) this else prefix / this
|
||||
|
||||
@DslMarker
|
||||
annotation class Builder
|
||||
|
||||
@Builder
|
||||
class ListBuilder<T> {
|
||||
private val irs = mutableListOf<T>()
|
||||
|
||||
operator fun T.unaryPlus() {
|
||||
irs += this
|
||||
}
|
||||
|
||||
fun addIfNotNull(value: T?) {
|
||||
if (value != null) irs += value
|
||||
}
|
||||
|
||||
operator fun List<T>.unaryPlus() {
|
||||
irs += this
|
||||
}
|
||||
|
||||
// out here for working with vararg arguments
|
||||
operator fun Array<out T>.unaryPlus() {
|
||||
irs += this
|
||||
}
|
||||
|
||||
fun build() = irs.toList()
|
||||
}
|
||||
|
||||
fun <T> buildList(builder: ListBuilder<T>.() -> Unit) =
|
||||
ListBuilder<T>().apply(builder).build()
|
||||
|
||||
object RandomIdGenerator {
|
||||
private val chars = ('a'..'z') + ('A'..'Z') + ('0'..'9')
|
||||
private const val idLength = 8
|
||||
fun generate() = (0 until idLength).joinToString(separator = "") {
|
||||
chars[Random.nextInt(chars.size)].toString()
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.safeAs
|
||||
|
||||
interface IR
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.TaskResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.map
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.safeAs
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.sequence
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.BuildScriptDependencyIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.BuildScriptIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.BuildScriptRepositoryIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.GradleIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.DefaultTargetConfigurationIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.BuildFilePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.MavenPrinter
|
||||
import java.nio.file.Path
|
||||
|
||||
data class BuildFileIR(
|
||||
val name: String,
|
||||
val directoryPath: Path,
|
||||
val modules: ModulesStructureIR,
|
||||
val pom: PomIR,
|
||||
override val irs: List<BuildSystemIR>
|
||||
) : BuildSystemIR, IrsOwner {
|
||||
override fun withReplacedIrs(irs: List<BuildSystemIR>): BuildFileIR = copy(irs = irs)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun withModulesUpdated(updater: (ModuleIR) -> TaskResult<ModuleIR>): TaskResult<BuildFileIR> =
|
||||
modules.withModulesUpdated(updater).map { newModules -> copy(modules = newModules) }
|
||||
|
||||
override fun BuildFilePrinter.render() = when (this) {
|
||||
is GradlePrinter -> {
|
||||
irsOfTypeOrNull<BuildScriptIR>()?.let { buildScriptIrs ->
|
||||
sectionCall("buildscript", needIndent = true) {
|
||||
sectionCall("repositories", buildScriptIrs.filterIsInstance<BuildScriptRepositoryIR>())
|
||||
nlIndented()
|
||||
sectionCall("dependencies", buildScriptIrs.filterIsInstance<BuildScriptDependencyIR>())
|
||||
}
|
||||
}
|
||||
sectionCall("plugins", irsOfType<BuildSystemPluginIR>()); nlIndented()
|
||||
pom.render(this); nl()
|
||||
sectionCall("repositories", irsOfType<RepositoryIR>())
|
||||
nl()
|
||||
irsOfTypeOrNull<FreeIR>()?.let { freeIrs ->
|
||||
freeIrs.listNl()
|
||||
nlIndented()
|
||||
}
|
||||
modules.render(this)
|
||||
}
|
||||
is MavenPrinter -> pom {
|
||||
pom.render(this)
|
||||
singleLineNode("packaging") { +"jar" }
|
||||
|
||||
nl()
|
||||
singleLineNode("name") { +name }
|
||||
nl()
|
||||
|
||||
node("properties") {
|
||||
singleLineNode("project.build.sourceEncoding") { +"UTF-8" }
|
||||
singleLineNode("kotlin.code.style") { +"official" }
|
||||
}
|
||||
|
||||
val plugins = irsOfType<BuildSystemPluginIR>().takeIf { it.isNotEmpty() }
|
||||
|
||||
if (plugins != null) {
|
||||
nl()
|
||||
node("build") {
|
||||
singleLineNode("sourceDirectory") { +"src/main/kotlin" }
|
||||
singleLineNode("testSourceDirectory") { +"src/test/kotlin" }
|
||||
node("plugins") {
|
||||
plugins.listNl()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modules.render(this)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ModulesStructureIR : BuildSystemIR, IrsOwner {
|
||||
abstract val modules: List<ModuleIR>
|
||||
|
||||
|
||||
abstract fun withModules(modules: List<ModuleIR>): ModulesStructureIR
|
||||
fun withModulesUpdated(updater: (ModuleIR) -> TaskResult<ModuleIR>): TaskResult<ModulesStructureIR> =
|
||||
modules.map { updater(it) }
|
||||
.sequence()
|
||||
.map {
|
||||
withModules(it)
|
||||
}
|
||||
}
|
||||
|
||||
data class MultiplatformModulesStructureIR(
|
||||
val targets: List<BuildSystemIR>,
|
||||
override val modules: List<SourcesetModuleIR>,
|
||||
override val irs: List<BuildSystemIR>
|
||||
) : GradleIR, ModulesStructureIR() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun withModules(modules: List<ModuleIR>) = copy(modules = modules as List<SourcesetModuleIR>)
|
||||
|
||||
override fun withReplacedIrs(irs: List<BuildSystemIR>): MultiplatformModulesStructureIR = copy(irs = irs)
|
||||
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
sectionCall("kotlin") {
|
||||
targets.filterNot {
|
||||
it.safeAs<DefaultTargetConfigurationIR>()?.targetAccess?.type == ModuleSubType.common
|
||||
}.listNl()
|
||||
nlIndented()
|
||||
sectionCall("sourceSets") {
|
||||
modules.listNl()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class SingleplatformModulesStructureWithSingleModuleIR(
|
||||
val module: SingleplatformModuleIR,
|
||||
override val irs: List<BuildSystemIR>
|
||||
) : ModulesStructureIR() {
|
||||
override val modules: List<SingleplatformModuleIR> = listOf(module)
|
||||
override fun withReplacedIrs(irs: List<BuildSystemIR>): SingleplatformModulesStructureWithSingleModuleIR =
|
||||
copy(irs = irs)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun withModules(modules: List<ModuleIR>) =
|
||||
copy(module = modules.single() as SingleplatformModuleIR)
|
||||
|
||||
override fun BuildFilePrinter.render() {
|
||||
module.render(this)
|
||||
}
|
||||
}
|
||||
|
||||
data class RootFileModuleStructureIR(override val irs: List<BuildSystemIR>) : ModulesStructureIR() {
|
||||
override val modules = emptyList<ModuleIR>()
|
||||
|
||||
override fun withReplacedIrs(irs: List<BuildSystemIR>): RootFileModuleStructureIR = copy(irs = irs)
|
||||
|
||||
override fun BuildFilePrinter.render() = when (this) {
|
||||
is MavenPrinter -> {
|
||||
irs.listNl()
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
override fun withModules(modules: List<ModuleIR>): RootFileModuleStructureIR = this
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.IR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.BuildFilePrinter
|
||||
|
||||
interface BuildSystemIR : IR {
|
||||
fun BuildFilePrinter.render()
|
||||
}
|
||||
|
||||
// IR element, which is not hardcoded into the parent IR structure and can be printed
|
||||
// In any place of the parent IR
|
||||
interface FreeIR : BuildSystemIR
|
||||
|
||||
interface KotlinIR : BuildSystemIR
|
||||
|
||||
fun BuildSystemIR.render(printer: BuildFilePrinter) {
|
||||
with(printer) { render() }
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ignore
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.GradleIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.BuildFilePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.MavenPrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||
|
||||
interface BuildSystemPluginIR : BuildSystemIR
|
||||
|
||||
interface DefaultBuildSystemPluginIR : BuildSystemPluginIR
|
||||
|
||||
data class ApplicationPluginIR(val mainClass: String) : DefaultBuildSystemPluginIR {
|
||||
override fun BuildFilePrinter.render() = when (this) {
|
||||
is GradlePrinter -> when (dsl) {
|
||||
GradlePrinter.GradleDsl.KOTLIN -> +"application"
|
||||
GradlePrinter.GradleDsl.GROOVY -> +"id 'application'"
|
||||
}
|
||||
is MavenPrinter -> node("plugin") {
|
||||
singleLineNode("groupId") { +"org.codehaus.mojo" }
|
||||
singleLineNode("artifactId") { +"exec-maven-plugin" }
|
||||
singleLineNode("version") { +"1.6.0" }
|
||||
|
||||
node("configuration") {
|
||||
singleLineNode("mainClass") { +mainClass }
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
data class GradleOnlyPluginByNameIR(val pluginId: String) : BuildSystemPluginIR, GradleIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
call("id") { +pluginId.quotified }
|
||||
}
|
||||
}
|
||||
|
||||
data class KotlinBuildSystemPluginIR(
|
||||
val type: Type,
|
||||
val version: Version?
|
||||
) : BuildSystemPluginIR {
|
||||
|
||||
override fun BuildFilePrinter.render() = when (this) {
|
||||
is GradlePrinter -> {
|
||||
when {
|
||||
type == Type.android -> call("id") { +"kotlin-android".quotified }
|
||||
dsl == GradlePrinter.GradleDsl.KOTLIN -> call("kotlin") { +type.toString().quotified }
|
||||
else -> call("id") { +"org.jetbrains.kotlin.$type".quotified }
|
||||
}
|
||||
version?.let {
|
||||
+" version "
|
||||
+it.toString().quotified
|
||||
}.ignore()
|
||||
}
|
||||
is MavenPrinter -> node("plugin") {
|
||||
singleLineNode("groupId") { +"org.jetbrains.kotlin" }
|
||||
singleLineNode("artifactId") { +"kotlin-maven-plugin" }
|
||||
singleLineNode("version") { +version.toString() }
|
||||
|
||||
node("executions") {
|
||||
node("execution") {
|
||||
singleLineNode("id") { +"compile" }
|
||||
singleLineNode("phase") { +"compile" }
|
||||
node("goals") {
|
||||
singleLineNode("goal") { +"compile" }
|
||||
}
|
||||
}
|
||||
|
||||
node("execution") {
|
||||
singleLineNode("id") { +"test-compile" }
|
||||
singleLineNode("phase") { +"test-compile" }
|
||||
node("goals") {
|
||||
singleLineNode("goal") { +"test-compile" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
@Suppress("EnumEntryName", "unused", "SpellCheckingInspection")
|
||||
enum class Type {
|
||||
jvm, multiplatform, android
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.safeAs
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.GradleIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.LibraryArtifact
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.MavenArtifact
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.NpmArtifact
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.BuildFilePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.MavenPrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.DefaultRepository.Companion.MAVEN_CENTRAL
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.ModulePath
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||
|
||||
interface DependencyIR : BuildSystemIR {
|
||||
val dependencyType: DependencyType
|
||||
|
||||
fun withDependencyType(type: DependencyType): DependencyIR
|
||||
}
|
||||
|
||||
data class ModuleDependencyIR(
|
||||
val path: ModulePath,
|
||||
val pomIR: PomIR,
|
||||
override val dependencyType: DependencyType
|
||||
) : DependencyIR {
|
||||
override fun withDependencyType(type: DependencyType): DependencyIR = copy(dependencyType = type)
|
||||
|
||||
override fun BuildFilePrinter.render() = when (this) {
|
||||
is GradlePrinter -> call(dependencyType.gradleName) {
|
||||
call("project", forceBrackets = true) {
|
||||
+path.parts.joinToString(separator = "") { ":$it" }.quotified
|
||||
}
|
||||
}
|
||||
is MavenPrinter -> node("dependency") {
|
||||
pomIR.render(this)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
data class GradleRootProjectDependencyIR(
|
||||
override val dependencyType: DependencyType
|
||||
) : DependencyIR, GradleIR {
|
||||
override fun withDependencyType(type: DependencyType): GradleRootProjectDependencyIR =
|
||||
copy(dependencyType = type)
|
||||
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
+"rootProject"
|
||||
}
|
||||
}
|
||||
|
||||
interface LibraryDependencyIR : DependencyIR {
|
||||
val artifact: LibraryArtifact
|
||||
val version: Version
|
||||
}
|
||||
|
||||
enum class DependencyType {
|
||||
MAIN, TEST
|
||||
}
|
||||
|
||||
fun SourcesetType.toDependencyType() = when (this) {
|
||||
SourcesetType.main -> DependencyType.MAIN
|
||||
SourcesetType.test -> DependencyType.TEST
|
||||
}
|
||||
|
||||
val DependencyType.gradleName
|
||||
get() = when (this) {
|
||||
DependencyType.MAIN -> "implementation"
|
||||
DependencyType.TEST -> "testImplementation"
|
||||
}
|
||||
|
||||
data class ArtifactBasedLibraryDependencyIR(
|
||||
override val artifact: LibraryArtifact,
|
||||
override val version: Version,
|
||||
override val dependencyType: DependencyType
|
||||
) : LibraryDependencyIR {
|
||||
override fun withDependencyType(type: DependencyType): ArtifactBasedLibraryDependencyIR =
|
||||
copy(dependencyType = type)
|
||||
|
||||
override fun BuildFilePrinter.render() = when (this) {
|
||||
is GradlePrinter -> call(dependencyType.gradleName) {
|
||||
with(artifact) {
|
||||
when (this) {
|
||||
is MavenArtifact -> +"$groupId:$artifactId:${version}".quotified
|
||||
is NpmArtifact -> {
|
||||
+"npm(${name.quotified}"
|
||||
+","
|
||||
+version.toString().quotified
|
||||
+")"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is MavenPrinter -> {
|
||||
node("dependency") {
|
||||
with(artifact as MavenArtifact) {
|
||||
singleLineNode("groupId") { +groupId }
|
||||
singleLineNode("artifactId") { +artifactId }
|
||||
}
|
||||
singleLineNode("version") { +version.toString() }
|
||||
if (dependencyType == DependencyType.TEST) {
|
||||
singleLineNode("scope") { +"test" }
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class KotlinLibraryDependencyIR(
|
||||
val name: String,
|
||||
override val version: Version,
|
||||
override val dependencyType: DependencyType
|
||||
) : LibraryDependencyIR {
|
||||
override val artifact: LibraryArtifact
|
||||
get() = MavenArtifact(
|
||||
MAVEN_CENTRAL,
|
||||
"org.jetbrains.kotlin",
|
||||
"kotlin-$name"
|
||||
)
|
||||
|
||||
override fun withDependencyType(type: DependencyType): KotlinLibraryDependencyIR =
|
||||
copy(dependencyType = type)
|
||||
|
||||
override fun BuildFilePrinter.render() {
|
||||
when (this) {
|
||||
is GradlePrinter -> call(dependencyType.gradleName) {
|
||||
+"kotlin("
|
||||
+name.quotified
|
||||
+")"
|
||||
}
|
||||
is MavenPrinter -> node("dependency") {
|
||||
singleLineNode("groupId") { +"org.jetbrains.kotlin" }
|
||||
singleLineNode("artifactId") { +"kotlin-stdlib" }
|
||||
singleLineNode("version") { +version.toString() }
|
||||
if (dependencyType == DependencyType.TEST) {
|
||||
singleLineNode("scope") { +"test" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val LibraryDependencyIR.isKotlinStdlib
|
||||
get() = safeAs<KotlinLibraryDependencyIR>()?.name?.contains("stdlib") == true
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem
|
||||
|
||||
|
||||
interface IrsOwner {
|
||||
val irs: List<BuildSystemIR>
|
||||
fun withReplacedIrs(irs: List<BuildSystemIR>): IrsOwner
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <I : IrsOwner> I.withIrs(irs: List<BuildSystemIR>) = withReplacedIrs(irs = this.irs + irs) as I
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <I : IrsOwner> I.withIrs(vararg irs: BuildSystemIR) = withReplacedIrs(irs = this.irs + irs) as I
|
||||
|
||||
|
||||
inline fun <reified I : BuildSystemIR> IrsOwner.irsOfType(): List<I> =
|
||||
irs.filterIsInstance<I>()
|
||||
|
||||
|
||||
inline fun <reified I : BuildSystemIR> IrsOwner.irsOfTypeOrNull() =
|
||||
irsOfType<I>().takeIf { it.isNotEmpty() }
|
||||
|
||||
|
||||
inline fun <reified T : BuildSystemIR> IrsOwner.firstIrOfType() =
|
||||
irs.firstOrNull { ir -> ir is T } as T?
|
||||
|
||||
fun IrsOwner.freeIrs(): List<FreeIR> =
|
||||
irs.filterIsInstance<FreeIR>()
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.GradleIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.BuildFilePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.MavenPrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.templates.Template
|
||||
import java.nio.file.Path
|
||||
|
||||
sealed class ModuleIR : IrsOwner, BuildSystemIR {
|
||||
abstract val name: String
|
||||
abstract val path: Path
|
||||
abstract val type: ModuleType
|
||||
}
|
||||
|
||||
|
||||
interface SourcesetIR : BuildSystemIR {
|
||||
val sourcesetType: SourcesetType
|
||||
val path: Path
|
||||
val template: Template?
|
||||
val original: Sourceset
|
||||
}
|
||||
|
||||
data class SingleplatformModuleIR(
|
||||
override val name: String,
|
||||
override val path: Path,
|
||||
override val irs: List<BuildSystemIR>,
|
||||
override val type: ModuleType,
|
||||
val sourcesets: List<SingleplatformSourcesetIR>
|
||||
) : ModuleIR() {
|
||||
override fun withReplacedIrs(irs: List<BuildSystemIR>): SingleplatformModuleIR = copy(irs = irs)
|
||||
|
||||
override fun BuildFilePrinter.render() = when (this) {
|
||||
is GradlePrinter -> {
|
||||
indent(); sectionCall("dependencies", irsOfType<DependencyIR>())
|
||||
}
|
||||
is MavenPrinter -> node("dependencies") {
|
||||
irsOfType<DependencyIR>().listNl()
|
||||
sourcesets.forEach { sourceset ->
|
||||
sourceset.irsOfType<DependencyIR>().listNl()
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
data class SingleplatformSourcesetIR(
|
||||
override val sourcesetType: SourcesetType,
|
||||
override val path: Path,
|
||||
override val irs: List<BuildSystemIR>,
|
||||
override val template: Template?,
|
||||
override val original: Sourceset
|
||||
) : SourcesetIR, IrsOwner {
|
||||
override fun withReplacedIrs(irs: List<BuildSystemIR>): SingleplatformSourcesetIR = copy(irs = irs)
|
||||
override fun BuildFilePrinter.render() = Unit
|
||||
}
|
||||
|
||||
data class SourcesetModuleIR(
|
||||
override val name: String,
|
||||
override val path: Path,
|
||||
override val irs: List<BuildSystemIR>,
|
||||
override val type: ModuleType,
|
||||
override val sourcesetType: SourcesetType,
|
||||
override val template: Template?,
|
||||
override val original: Sourceset
|
||||
) : SourcesetIR, GradleIR, ModuleIR() {
|
||||
override fun withReplacedIrs(irs: List<BuildSystemIR>): SourcesetModuleIR = copy(irs = irs)
|
||||
|
||||
override fun GradlePrinter.renderGradle() = getting(name, prefix = null) {
|
||||
val dependencies = irsOfType<DependencyIR>()
|
||||
val needBody = dependencies.isNotEmpty() || dsl == GradlePrinter.GradleDsl.GROOVY
|
||||
if (needBody) {
|
||||
+" "
|
||||
inBrackets {
|
||||
if (dependencies.isNotEmpty()) {
|
||||
indent()
|
||||
sectionCall("dependencies", dependencies)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.BuildFilePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.MavenPrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||
|
||||
data class PomIR(
|
||||
val artifactId: String,
|
||||
val groupId: String,
|
||||
val version: Version
|
||||
) : BuildSystemIR {
|
||||
override fun BuildFilePrinter.render() = when (this) {
|
||||
is GradlePrinter -> {
|
||||
+"group = "; +groupId.quotified; nl()
|
||||
+"version = "; +version.toString().quotified; nl()
|
||||
}
|
||||
is MavenPrinter -> {
|
||||
singleLineNode("artifactId") { +artifactId }
|
||||
singleLineNode("groupId") { +groupId }
|
||||
singleLineNode("version") { +version.toString() }
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.BuildFilePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.DefaultRepository
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Repository
|
||||
|
||||
data class RepositoryIR(val repository: Repository) : BuildSystemIR {
|
||||
override fun BuildFilePrinter.render() = when (this) {
|
||||
is GradlePrinter -> when (repository) {
|
||||
is DefaultRepository -> {
|
||||
+repository.type.asGradleName()
|
||||
+"()"
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun DefaultRepository.Type.asGradleName() = when (this) {
|
||||
DefaultRepository.Type.JCENTER -> "jcenter"
|
||||
DefaultRepository.Type.MAVEN_CENTRAL -> "mavenCentral"
|
||||
DefaultRepository.Type.GOOGLE -> "google"
|
||||
DefaultRepository.Type.GRADLE_PLUGIN_PORTAL -> "gradlePluginPortal"
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.FreeIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
|
||||
interface AndroidIR : GradleIR
|
||||
|
||||
//TODO parematrize
|
||||
data class AndroidConfigIR(val artifactId: String) : AndroidIR, FreeIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
sectionCall("android", needIndent = true) {
|
||||
call("compileSdkVersion") { +"29" }; nlIndented() // TODO dehardcode
|
||||
sectionCall("defaultConfig", needIndent = true) {
|
||||
assignmentOrCall("applicationId") { +artifactId.quotified }; nlIndented()
|
||||
call("minSdkVersion") { +"24" }; nlIndented() // TODO dehardcode
|
||||
call("targetSdkVersion") { +"29" }; nlIndented() // TODO dehardcode
|
||||
assignmentOrCall("versionCode") { +"1" }; nlIndented()
|
||||
assignmentOrCall("versionName") { +"1.0".quotified }
|
||||
}
|
||||
nlIndented()
|
||||
sectionCall("buildTypes", needIndent = true) {
|
||||
val sectionIdentifier = when (dsl) {
|
||||
GradlePrinter.GradleDsl.KOTLIN -> """getByName("release")"""
|
||||
GradlePrinter.GradleDsl.GROOVY -> "release".quotified
|
||||
}
|
||||
sectionCall(sectionIdentifier, needIndent = true) {
|
||||
assignmentOrCall("isMinifyEnabled") { +"false" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ListBuilder
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.buildList
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.BuildSystemIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.render
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
|
||||
data class BodyIR(
|
||||
val body: List<BuildSystemIR>
|
||||
) : GradleIR {
|
||||
constructor(vararg body: BuildSystemIR) : this(body.toList())
|
||||
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
inBrackets {
|
||||
body.listNl()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun buildBody(builder: ListBuilder<BuildSystemIR>.() -> Unit) =
|
||||
BodyIR(buildList(builder))
|
||||
|
||||
fun BodyIR.renderIfNotEmpty(printer: GradlePrinter) =
|
||||
takeIf { it.isNotEmpty() }?.render(printer)
|
||||
|
||||
fun BodyIR.isEmpty() = body.isEmpty()
|
||||
fun BodyIR.isNotEmpty() = body.isNotEmpty()
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.BuildFilePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
|
||||
interface GradleIR : BuildSystemIR {
|
||||
fun GradlePrinter.renderGradle()
|
||||
|
||||
override fun BuildFilePrinter.render() {
|
||||
if (this is GradlePrinter) renderGradle()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class RawGradleIR(
|
||||
val renderer: GradlePrinter.() -> Unit
|
||||
) : GradleIR {
|
||||
override fun GradlePrinter.renderGradle() = renderer()
|
||||
}
|
||||
|
||||
data class CreateGradleValueIR(val name: String, val body: GradleIR) : GradleIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
when (dsl) {
|
||||
GradlePrinter.GradleDsl.KOTLIN -> {
|
||||
+"val "
|
||||
+name
|
||||
+" = "
|
||||
body.render(this)
|
||||
}
|
||||
GradlePrinter.GradleDsl.GROOVY -> {
|
||||
+"def "
|
||||
+name
|
||||
+" = "
|
||||
body.render(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class GradleCallIr(
|
||||
val name: String
|
||||
) : GradleIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
sectionCall(name) {}
|
||||
}
|
||||
}
|
||||
|
||||
data class GradleSectionIR(
|
||||
val name: String,
|
||||
val body: BodyIR
|
||||
) : GradleIR, FreeIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
+name
|
||||
+" "
|
||||
body.render(this)
|
||||
}
|
||||
}
|
||||
|
||||
data class GradleAssignmentIR(
|
||||
val target: String,
|
||||
val assignee: BuildSystemIR
|
||||
) : GradleIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
+"$target = "
|
||||
assignee.render(this)
|
||||
}
|
||||
}
|
||||
|
||||
data class GradleStringConstIR(
|
||||
val text: String
|
||||
) : GradleIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
+text.quotified
|
||||
}
|
||||
}
|
||||
|
||||
data class CompilationAccessIr(
|
||||
val targetName: String,
|
||||
val compilationName: String,
|
||||
val property: String?
|
||||
) : GradleIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
when (dsl) {
|
||||
GradlePrinter.GradleDsl.KOTLIN -> +"kotlin.$targetName().compilations[\"$compilationName\"]"
|
||||
GradlePrinter.GradleDsl.GROOVY -> +"kotlin.$targetName().compilations.$compilationName"
|
||||
}
|
||||
property?.let { +".$it" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface BuildScriptIR : BuildSystemIR
|
||||
|
||||
data class BuildScriptDependencyIR(val dependencyIR: GradleIR) : BuildScriptIR, BuildSystemIR by dependencyIR
|
||||
data class BuildScriptRepositoryIR(val repositoryIR: RepositoryIR) : BuildScriptIR, BuildSystemIR by repositoryIR
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.FreeIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
|
||||
interface GradleTaskIR : GradleIR, FreeIR {
|
||||
val name: String
|
||||
val taskClass: String
|
||||
val body: BodyIR
|
||||
}
|
||||
|
||||
data class CreateGradleTaskIR(
|
||||
override val name: String,
|
||||
override val taskClass: String,
|
||||
override val body: BodyIR
|
||||
) : GradleTaskIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
when (dsl) {
|
||||
GradlePrinter.GradleDsl.GROOVY -> {
|
||||
+"tasks.create('$name', $taskClass)"
|
||||
}
|
||||
GradlePrinter.GradleDsl.KOTLIN -> {
|
||||
+"val $name by tasks.registering($taskClass::class)"
|
||||
}
|
||||
}
|
||||
+" "
|
||||
body.renderIfNotEmpty(this)
|
||||
}
|
||||
}
|
||||
|
||||
data class GetGradleTaskIR(
|
||||
override val name: String,
|
||||
override val taskClass: String,
|
||||
override val body: BodyIR
|
||||
) : GradleTaskIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
when (dsl) {
|
||||
GradlePrinter.GradleDsl.GROOVY -> {
|
||||
+"tasks.getByName('$name')"
|
||||
}
|
||||
GradlePrinter.GradleDsl.KOTLIN -> {
|
||||
+"tasks.getByName<$taskClass>(${name.quotified})"
|
||||
}
|
||||
}
|
||||
+" "
|
||||
body.renderIfNotEmpty(this)
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.BuildSystemIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.BuildFilePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.MavenPrinter
|
||||
|
||||
interface MavenIR : BuildSystemIR {
|
||||
fun MavenPrinter.render()
|
||||
|
||||
override fun BuildFilePrinter.render() {
|
||||
if (this is MavenPrinter) render()
|
||||
}
|
||||
}
|
||||
|
||||
data class ModulesDependencyMavenIR(val dependencies: List<String>) : MavenIR {
|
||||
override fun MavenPrinter.render() {
|
||||
node("modules") {
|
||||
dependencies.forEach { dependency ->
|
||||
singleLineNode("module") { +dependency }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.BuildSystemIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.IrsOwner
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.KotlinIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.GradleIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
|
||||
interface MultiplatformIR : GradleIR, KotlinIR
|
||||
|
||||
interface TargetIR: MultiplatformIR
|
||||
|
||||
data class TargetAccessIR(
|
||||
val type: ModuleSubType,
|
||||
val nonDefaultName: String? // `null` stands for default target name
|
||||
) : TargetIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
+type.toString()
|
||||
par {
|
||||
nonDefaultName?.let { +it.quotified }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface TargetConfigurationIR : MultiplatformIR, IrsOwner
|
||||
|
||||
data class DefaultTargetConfigurationIR(
|
||||
val targetAccess: TargetAccessIR,
|
||||
override val irs: List<BuildSystemIR>
|
||||
) : TargetConfigurationIR {
|
||||
override fun withReplacedIrs(irs: List<BuildSystemIR>): DefaultTargetConfigurationIR =
|
||||
copy(irs = irs)
|
||||
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
+targetAccess.type.toString()
|
||||
if (irs.isEmpty() || targetAccess.nonDefaultName != null) par {
|
||||
targetAccess.nonDefaultName?.let { +it.quotified }
|
||||
}
|
||||
if (irs.isNotEmpty()) {
|
||||
+" "; inBrackets { irs.listNl() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class NonDefaultTargetConfigurationIR(
|
||||
val name: String,
|
||||
override val irs: List<BuildSystemIR>
|
||||
) : TargetConfigurationIR {
|
||||
override fun withReplacedIrs(irs: List<BuildSystemIR>): NonDefaultTargetConfigurationIR =
|
||||
copy(irs = irs)
|
||||
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
+name
|
||||
when (dsl) {
|
||||
GradlePrinter.GradleDsl.KOTLIN -> +".apply"
|
||||
GradlePrinter.GradleDsl.GROOVY-> +".with"
|
||||
}
|
||||
+" "; inBrackets { irs.listNl() }
|
||||
}
|
||||
}
|
||||
|
||||
data class CompilationIR(
|
||||
val name: String,
|
||||
override val irs: List<BuildSystemIR>
|
||||
) : MultiplatformIR, IrsOwner {
|
||||
override fun withReplacedIrs(irs: List<BuildSystemIR>) = copy(irs = irs)
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
getting(name, "compilations") { irs.listNl() }
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.GradleIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
|
||||
class NativeTargetInternalIR(
|
||||
val mainClassFqName: String?
|
||||
) : GradleIR {
|
||||
override fun GradlePrinter.renderGradle() {
|
||||
sectionCall("binaries") {
|
||||
indent()
|
||||
sectionCall("executable") {
|
||||
mainClassFqName?.let { mainClass ->
|
||||
indent(); assignment("entryPoint") { +mainClass.quotified }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.library
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Repository
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||
|
||||
sealed class LibraryArtifact
|
||||
|
||||
data class MavenArtifact(
|
||||
val repository: Repository,
|
||||
val groupId: String,
|
||||
val artifactId: String
|
||||
) : LibraryArtifact()
|
||||
|
||||
data class NpmArtifact(
|
||||
val name: String
|
||||
) : LibraryArtifact()
|
||||
|
||||
|
||||
sealed class LibraryDescriptor {
|
||||
abstract val artifact: LibraryArtifact
|
||||
abstract val type: LibraryType
|
||||
abstract val version: Version
|
||||
}
|
||||
|
||||
data class MavenLibraryDescriptor(
|
||||
override val artifact: MavenArtifact,
|
||||
override val type: LibraryType,
|
||||
override val version: Version
|
||||
) : LibraryDescriptor()
|
||||
|
||||
|
||||
data class NpmLibraryDescriptor(
|
||||
override val artifact: NpmArtifact,
|
||||
override val version: Version
|
||||
) : LibraryDescriptor() {
|
||||
override val type: LibraryType get() = LibraryType.NPM
|
||||
}
|
||||
|
||||
|
||||
sealed class LibraryType(
|
||||
val supportJvm: Boolean,
|
||||
val supportJs: Boolean,
|
||||
val supportNative: Boolean
|
||||
) {
|
||||
object JVM_ONLY : LibraryType(supportJvm = true, supportJs = false, supportNative = false)
|
||||
object JS_ONLY : LibraryType(supportJvm = false, supportJs = true, supportNative = false)
|
||||
object NPM : LibraryType(supportJvm = false, supportJs = true, supportNative = false)
|
||||
object MULTIPLATFORM : LibraryType(supportJvm = true, supportJs = true, supportNative = true)
|
||||
}
|
||||
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ModuleConfiguratorSetting
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.AndroidService
|
||||
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
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.BuildScriptRepositoryIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.RawGradleIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.MavenArtifact
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GradlePlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleConfigurationData
|
||||
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.DefaultRepository
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
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 org.jetbrains.kotlin.tools.projectWizard.templates.FileTemplateDescriptor
|
||||
import java.nio.file.Path
|
||||
|
||||
object AndroidSinglePlatformModuleConfigurator : ModuleConfiguratorWithSettings(),
|
||||
SinglePlatformModuleConfigurator,
|
||||
AndroidModuleConfigurator {
|
||||
override val moduleType = ModuleType.jvm
|
||||
override val id = "android"
|
||||
override val suggestedModuleName = "android"
|
||||
override val greyText = "Requires Android SDK"
|
||||
|
||||
val androidSdkPath by pathSetting("Android SDK Path", neededAtPhase = GenerationPhase.PROJECT_GENERATION) {
|
||||
shouldExists()
|
||||
|
||||
validate { path ->
|
||||
ValidationResult.create(
|
||||
service<AndroidService>().isValidAndroidSdk(
|
||||
path
|
||||
),
|
||||
"Path should point to valid Android SDK location"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createBuildFileIRs(configurationData: ModuleConfigurationData, module: Module) =
|
||||
buildList<BuildSystemIR> {
|
||||
+GradleOnlyPluginByNameIR("com.android.application")
|
||||
|
||||
// it is explicitly here instead of by `createKotlinPluginIR` as it should be after `com.android.application`
|
||||
+KotlinBuildSystemPluginIR(
|
||||
KotlinBuildSystemPluginIR.Type.android,
|
||||
version = null // Version is already present in the parent buildfile
|
||||
)
|
||||
+GradleOnlyPluginByNameIR("kotlin-android-extensions")
|
||||
+AndroidConfigIR(configurationData.pomIr.artifactId)
|
||||
+listOf(
|
||||
DefaultRepository.GRADLE_PLUGIN_PORTAL,
|
||||
DefaultRepository.GOOGLE,
|
||||
DefaultRepository.JCENTER
|
||||
).map(::RepositoryIR)
|
||||
}
|
||||
|
||||
override fun createRootBuildFileIrs(configurationData: ModuleConfigurationData) = buildList<BuildSystemIR> {
|
||||
+listOf(
|
||||
DefaultRepository.GRADLE_PLUGIN_PORTAL,
|
||||
DefaultRepository.GOOGLE,
|
||||
DefaultRepository.JCENTER
|
||||
).map { BuildScriptRepositoryIR(RepositoryIR(it)) }
|
||||
+listOf(
|
||||
RawGradleIR { call("classpath") { +"com.android.tools.build:gradle:3.2.1".quotified } },
|
||||
RawGradleIR {
|
||||
call("classpath") {
|
||||
call("kotlin", forceBrackets = true) {
|
||||
+"gradle-plugin".quotified
|
||||
+", "
|
||||
+configurationData.kotlinVersion.toString().quotified
|
||||
}
|
||||
}
|
||||
}
|
||||
).map(::BuildScriptDependencyIR)
|
||||
}
|
||||
|
||||
override fun TaskRunningContext.runArbitraryTask(
|
||||
configurationData: ModuleConfigurationData,
|
||||
module: Module,
|
||||
modulePath: Path
|
||||
): TaskResult<Unit> = withSettingsOf(module) {
|
||||
val path = androidSdkPath.reference.settingValue
|
||||
GradlePlugin::localProperties.addValues(
|
||||
"sdk.dir" to path
|
||||
)
|
||||
} andThen computeM {
|
||||
val javaPackage = module.javaPackage(configurationData.pomIr)
|
||||
TemplatesPlugin::addFileTemplates.execute(
|
||||
listOf(
|
||||
FileTemplate(FileTemplateDescriptors.activityMainXml, modulePath, "package" to javaPackage),
|
||||
FileTemplate(FileTemplateDescriptors.androidManifestXml, modulePath, "package" to javaPackage),
|
||||
FileTemplate(FileTemplateDescriptors.mainActivityKt(javaPackage), modulePath, "package" to javaPackage)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun createModuleIRs(configurationData: ModuleConfigurationData, module: Module): List<BuildSystemIR> =
|
||||
buildList {
|
||||
+ArtifactBasedLibraryDependencyIR(
|
||||
MavenArtifact(DefaultRepository.GOOGLE, "androidx.appcompat", "appcompat"),
|
||||
version = Version.fromString("1.1.0"),
|
||||
dependencyType = DependencyType.MAIN
|
||||
)
|
||||
|
||||
+ArtifactBasedLibraryDependencyIR(
|
||||
MavenArtifact(DefaultRepository.GOOGLE, "androidx.core", "core-ktx"),
|
||||
version = Version.fromString("1.1.0"),
|
||||
dependencyType = DependencyType.MAIN
|
||||
)
|
||||
|
||||
+KotlinLibraryDependencyIR("stdlib-jdk7", configurationData.kotlinVersion, DependencyType.MAIN)
|
||||
|
||||
+ArtifactBasedLibraryDependencyIR(
|
||||
MavenArtifact(DefaultRepository.GOOGLE, "androidx.constraintlayout", "constraintlayout"),
|
||||
version = Version.fromString("1.1.3"),
|
||||
dependencyType = DependencyType.MAIN
|
||||
)
|
||||
}
|
||||
|
||||
override val settings: List<ModuleConfiguratorSetting<*, *>> =
|
||||
listOf(androidSdkPath)
|
||||
|
||||
private object FileTemplateDescriptors {
|
||||
val activityMainXml = FileTemplateDescriptor(
|
||||
"android/activity_main.xml.vm",
|
||||
"src" / "main" / "res" / "layout" / "activity_main.xml"
|
||||
)
|
||||
|
||||
val androidManifestXml = FileTemplateDescriptor(
|
||||
"android/AndroidManifest.xml.vm",
|
||||
"src" / "main" / "AndroidManifest.xml"
|
||||
)
|
||||
|
||||
fun mainActivityKt(javaPackage: JavaPackage) = FileTemplateDescriptor(
|
||||
"android/MainActivity.kt.vm",
|
||||
"src" / "main" / "java" / javaPackage.asPath() / "MainActivity.kt"
|
||||
)
|
||||
}
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.Identificator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.SettingsOwner
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.cached
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.BuildSystemIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.KotlinBuildSystemPluginIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleConfigurationData
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||
import java.nio.file.Path
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
|
||||
class ModuleSettingsEnvironment(private val configurator: ModuleConfigurator, private val moduleId: Identificator) {
|
||||
val <V : Any, T : SettingType<V>> ModuleConfiguratorSetting<V, T>.reference
|
||||
get() = ModuleConfiguratorSettingReference(configurator, moduleId, this)
|
||||
}
|
||||
|
||||
fun <T> withSettingsOf(
|
||||
moduleId: Identificator,
|
||||
configurator: ModuleConfigurator,
|
||||
function: ModuleSettingsEnvironment.() -> T
|
||||
): T = function(ModuleSettingsEnvironment(configurator, moduleId))
|
||||
|
||||
fun <T> withSettingsOf(
|
||||
module: Module,
|
||||
configurator: ModuleConfigurator = module.configurator,
|
||||
function: ModuleSettingsEnvironment.() -> T
|
||||
): T = function(ModuleSettingsEnvironment(configurator, module.identificator))
|
||||
|
||||
|
||||
abstract class ModuleConfiguratorWithSettings : ModuleConfigurator, SettingsOwner {
|
||||
override fun <V : Any, T : SettingType<V>> settingDelegate(
|
||||
create: (path: String) -> SettingBuilder<V, T>
|
||||
): ReadOnlyProperty<Any?, ModuleConfiguratorSetting<V, T>> = cached { name ->
|
||||
ModuleConfiguratorSetting(create(name).buildInternal())
|
||||
}
|
||||
|
||||
abstract val settings: List<ModuleConfiguratorSetting<*, *>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <V : DisplayableSettingItem> dropDownSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: DropDownSettingType.Builder<V>.() -> Unit
|
||||
): ReadOnlyProperty<Any, ModuleConfiguratorSetting<V, DropDownSettingType<V>>> =
|
||||
super.dropDownSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
parser,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, ModuleConfiguratorSetting<V, DropDownSettingType<V>>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun stringSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: StringSettingType.Builder.() -> Unit
|
||||
): ReadOnlyProperty<Any, ModuleConfiguratorSetting<String, StringSettingType>> =
|
||||
super.stringSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, ModuleConfiguratorSetting<String, StringSettingType>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun booleanSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: BooleanSettingType.Builder.() -> Unit
|
||||
): ReadOnlyProperty<Any, ModuleConfiguratorSetting<Boolean, BooleanSettingType>> =
|
||||
super.booleanSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, ModuleConfiguratorSetting<Boolean, BooleanSettingType>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <V : Any> valueSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: ValueSettingType.Builder<V>.() -> Unit
|
||||
): ReadOnlyProperty<Any, ModuleConfiguratorSetting<V, ValueSettingType<V>>> =
|
||||
super.valueSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
parser,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, ModuleConfiguratorSetting<V, ValueSettingType<V>>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun versionSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: VersionSettingType.Builder.() -> Unit
|
||||
): ReadOnlyProperty<Any, ModuleConfiguratorSetting<Version, VersionSettingType>> =
|
||||
super.versionSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, ModuleConfiguratorSetting<Version, VersionSettingType>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <V : Any> listSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: ListSettingType.Builder<V>.() -> Unit
|
||||
): ReadOnlyProperty<Any, ModuleConfiguratorSetting<List<V>, ListSettingType<V>>> =
|
||||
super.listSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
parser,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, ModuleConfiguratorSetting<List<V>, ListSettingType<V>>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun pathSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: PathSettingType.Builder.() -> Unit
|
||||
): ReadOnlyProperty<Any, ModuleConfiguratorSetting<Path, PathSettingType>> =
|
||||
super.pathSetting(
|
||||
title,
|
||||
neededAtPhase,
|
||||
init
|
||||
) as ReadOnlyProperty<Any, ModuleConfiguratorSetting<Path, PathSettingType>>
|
||||
|
||||
inline fun <reified E> enumSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
crossinline init: DropDownSettingType.Builder<E>.() -> Unit = {}
|
||||
) where E : Enum<E>, E : DisplayableSettingItem = dropDownSetting<E>(title, neededAtPhase, enumParser()) {
|
||||
values = enumValues<E>().asList()
|
||||
init()
|
||||
}
|
||||
}
|
||||
|
||||
val ModuleConfigurator.settings
|
||||
get() = when (this) {
|
||||
is ModuleConfiguratorWithSettings -> settings
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
val Module.configuratorSettings
|
||||
get() = configurator.settings.map { setting ->
|
||||
ModuleConfiguratorSettingReference(configurator, this, setting)
|
||||
}
|
||||
|
||||
interface ModuleConfigurator : DisplayableSettingItem, EntitiesOwnerDescriptor {
|
||||
val moduleKind: ModuleKind
|
||||
val moduleType: ModuleType
|
||||
override val text: String
|
||||
get() = id
|
||||
|
||||
val suggestedModuleName: String? get() = null
|
||||
val canContainSubModules: Boolean get() = false
|
||||
|
||||
fun createBuildFileIRs(configurationData: ModuleConfigurationData, module: Module): List<BuildSystemIR> =
|
||||
emptyList()
|
||||
|
||||
fun createModuleIRs(configurationData: ModuleConfigurationData, module: Module): List<BuildSystemIR> =
|
||||
emptyList()
|
||||
|
||||
fun createRootBuildFileIrs(configurationData: ModuleConfigurationData): List<BuildSystemIR> = emptyList()
|
||||
fun createKotlinPluginIR(configurationData: ModuleConfigurationData, module: Module): KotlinBuildSystemPluginIR? =
|
||||
null
|
||||
|
||||
fun TaskRunningContext.runArbitraryTask(
|
||||
configurationData: ModuleConfigurationData,
|
||||
module: Module,
|
||||
modulePath: Path
|
||||
): TaskResult<Unit> = UNIT_SUCCESS
|
||||
|
||||
companion object {
|
||||
val ALL = buildList<ModuleConfigurator> {
|
||||
+RealNativeTargetConfigurator.configurators
|
||||
+NativeForCurrentSystemTarget
|
||||
+JsBrowserTargetConfigurator
|
||||
+JsNodeTargetConfigurator
|
||||
+CommonTargetConfigurator
|
||||
+JvmTargetConfigurator
|
||||
+AndroidTargetConfigurator
|
||||
+MppModuleConfigurator
|
||||
+JvmSinglePlatformModuleConfigurator
|
||||
+AndroidSinglePlatformModuleConfigurator
|
||||
+IOSSinglePlatformModuleConfigurator
|
||||
}
|
||||
|
||||
init {
|
||||
ALL.groupBy(ModuleConfigurator::id)
|
||||
.forEach { (id, configurators) -> assert(configurators.size == 1) { id } }
|
||||
}
|
||||
|
||||
val BY_ID = ALL.associateBy(ModuleConfigurator::id)
|
||||
val BY_MODULE_KIND = ALL.groupBy(ModuleConfigurator::moduleKind)
|
||||
|
||||
fun getParser(moduleIdentificator: Identificator): Parser<ModuleConfigurator> = mapParser { map, path ->
|
||||
val (id) = map.parseValue<String>(path, "name")
|
||||
val (configurator) = BY_ID[id].toResult { ConfiguratorNotFoundError(id) }
|
||||
val (settingsWithValues) = configurator.settings.mapComputeM { setting ->
|
||||
val (settingValue) = map[setting.path].toResult { ParseError("No value was found for a key `$path.${setting.path}`") }
|
||||
val reference = withSettingsOf(moduleIdentificator, configurator) { setting.reference }
|
||||
setting.type.parse(this, settingValue, setting.path).map { reference to it }
|
||||
}.sequence()
|
||||
updateState { it.withSettings(settingsWithValues) }
|
||||
configurator
|
||||
} or valueParserM { value, path ->
|
||||
val (id) = value.parseAs<String>(path)
|
||||
BY_ID[id].toResult { ConfiguratorNotFoundError(id) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.buildList
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.BuildSystemIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.CreateGradleValueIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.RawGradleIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.NativeTargetInternalIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.NonDefaultTargetConfigurationIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.printer.GradlePrinter
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
|
||||
interface NativeTargetConfigurator : TargetConfigurator {
|
||||
override fun createInnerTargetIrs(module: Module): List<BuildSystemIR> = buildList {
|
||||
+NativeTargetInternalIR("MAIN CLASS")
|
||||
}
|
||||
}
|
||||
|
||||
class RealNativeTargetConfigurator private constructor(
|
||||
override val moduleSubType: ModuleSubType
|
||||
) : NativeTargetConfigurator, SimpleTargetConfigurator {
|
||||
companion object {
|
||||
val configurators = ModuleSubType.values()
|
||||
.filter { it.moduleType == ModuleType.native }
|
||||
.map(::RealNativeTargetConfigurator)
|
||||
|
||||
val configuratorsByModuleType = configurators.associateBy { it.moduleSubType }
|
||||
}
|
||||
}
|
||||
|
||||
object NativeForCurrentSystemTarget : NativeTargetConfigurator, SingleCoexistenceTargetConfigurator {
|
||||
override val moduleType = ModuleType.native
|
||||
override val id = "nativeForCurrentSystem"
|
||||
override val text = "For Current System"
|
||||
|
||||
|
||||
override fun createTargetIrs(module: Module): List<BuildSystemIR> {
|
||||
val moduleName = module.name
|
||||
return buildList {
|
||||
+CreateGradleValueIR("hostOs", RawGradleIR { +"System.getProperty(\"os.name\")" })
|
||||
+CreateGradleValueIR("isMingwX64", RawGradleIR { +"hostOs.startsWith(\"Windows\")" })
|
||||
|
||||
//TODO do not use RawGradleIR here
|
||||
+RawGradleIR {
|
||||
when (dsl) {
|
||||
GradlePrinter.GradleDsl.KOTLIN -> {
|
||||
+"val $DEFAULT_TARGET_VARIABLE_NAME = when "
|
||||
inBrackets {
|
||||
indent()
|
||||
+"""hostOs == "Mac OS X" -> macosX64("$moduleName")"""; nlIndented()
|
||||
+"""hostOs == "Linux" -> linuxX64("$moduleName")"""; nlIndented()
|
||||
+"""isMingwX64 -> mingwX64("$moduleName")"""; nlIndented()
|
||||
+"""else -> throw GradleException("Host OS is not supported in Kotlin/Native.")"""
|
||||
}
|
||||
}
|
||||
GradlePrinter.GradleDsl.GROOVY -> {
|
||||
+"""org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTests $DEFAULT_TARGET_VARIABLE_NAME"""; nlIndented()
|
||||
+"""if (hostOs == "Mac OS X") $DEFAULT_TARGET_VARIABLE_NAME = macosX64('$moduleName')"""; nlIndented()
|
||||
+"""else if (hostOs == "Linux") $DEFAULT_TARGET_VARIABLE_NAME = linuxX64("$moduleName")"""; nlIndented()
|
||||
+"""else if (isMingwX64) return $DEFAULT_TARGET_VARIABLE_NAME = mingwX64("$moduleName")"""; nlIndented()
|
||||
+"""else throw new GradleException("Host OS is not supported in Kotlin/Native.")""";
|
||||
}
|
||||
}
|
||||
nl()
|
||||
}
|
||||
|
||||
|
||||
|
||||
+NonDefaultTargetConfigurationIR(
|
||||
DEFAULT_TARGET_VARIABLE_NAME,
|
||||
createInnerTargetIrs(module)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val DEFAULT_TARGET_VARIABLE_NAME = "nativeTarget"
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
||||
|
||||
object TargetConfigurationGroups {
|
||||
val JVM = FinalTargetConfiguratorGroup(
|
||||
ModuleType.jvm.projectTypeName,
|
||||
ModuleType.jvm,
|
||||
listOf(
|
||||
JvmTargetConfigurator,
|
||||
AndroidTargetConfigurator
|
||||
)
|
||||
)
|
||||
|
||||
val JS = FinalTargetConfiguratorGroup(
|
||||
ModuleType.js.projectTypeName,
|
||||
ModuleType.js,
|
||||
listOf(
|
||||
JsBrowserTargetConfigurator,
|
||||
JsNodeTargetConfigurator
|
||||
)
|
||||
)
|
||||
|
||||
object NATIVE {
|
||||
val LINUX = FinalTargetConfiguratorGroup(
|
||||
"Linux",
|
||||
ModuleType.native,
|
||||
listOf(
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.linuxX64),
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.linuxArm32Hfp),
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.linuxMips32),
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.linuxMipsel32)
|
||||
)
|
||||
)
|
||||
|
||||
val WINDOWS = FinalTargetConfiguratorGroup(
|
||||
"Windows (MinGW)",
|
||||
ModuleType.native,
|
||||
listOf(
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.mingwX64),
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.mingwX86)
|
||||
)
|
||||
)
|
||||
|
||||
val MAC = FinalTargetConfiguratorGroup(
|
||||
"macOS",
|
||||
ModuleType.native,
|
||||
listOf(
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.macosX64)
|
||||
)
|
||||
)
|
||||
|
||||
val IOS = FinalTargetConfiguratorGroup(
|
||||
"iOS",
|
||||
ModuleType.native,
|
||||
listOf(
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.iosArm32),
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.iosArm64),
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.iosX64)
|
||||
)
|
||||
)
|
||||
|
||||
val ANDROID_NATIVE = FinalTargetConfiguratorGroup(
|
||||
"Android Native",
|
||||
ModuleType.native,
|
||||
listOf(
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.androidNativeArm64),
|
||||
RealNativeTargetConfigurator.configuratorsByModuleType.getValue(ModuleSubType.androidNativeArm32)
|
||||
)
|
||||
)
|
||||
|
||||
val ALL = StepTargetConfiguratorGroup(
|
||||
ModuleType.native.projectTypeName,
|
||||
ModuleType.native,
|
||||
listOf(
|
||||
NativeForCurrentSystemTarget,
|
||||
LINUX,
|
||||
WINDOWS,
|
||||
MAC,
|
||||
IOS,
|
||||
ANDROID_NATIVE
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val FIRST = FirstStepTargetConfiguratorGroup(
|
||||
listOf(
|
||||
CommonTargetConfigurator,
|
||||
JVM,
|
||||
NATIVE.ALL,
|
||||
JS
|
||||
)
|
||||
)
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.buildList
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.BuildSystemIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.RawGradleIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.DefaultTargetConfigurationIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.TargetAccessIR
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.ModuleKind
|
||||
|
||||
|
||||
interface TargetConfigurator : ModuleConfigurator {
|
||||
override val moduleKind get() = ModuleKind.target
|
||||
|
||||
fun canCoexistsWith(other: List<TargetConfigurator>): Boolean = true
|
||||
|
||||
fun createTargetIrs(module: Module): List<BuildSystemIR>
|
||||
fun createInnerTargetIrs(module: Module): List<BuildSystemIR> = emptyList()
|
||||
}
|
||||
|
||||
interface SingleCoexistenceTargetConfigurator : TargetConfigurator {
|
||||
override fun canCoexistsWith(other: List<TargetConfigurator>): Boolean =
|
||||
other.none { it == this }
|
||||
}
|
||||
|
||||
interface SimpleTargetConfigurator : TargetConfigurator {
|
||||
val moduleSubType: ModuleSubType
|
||||
override val moduleType get() = moduleSubType.moduleType
|
||||
override val id get() = "${moduleSubType.name}Target"
|
||||
override val text get() = moduleSubType.name.capitalize()
|
||||
|
||||
override val suggestedModuleName: String? get() = moduleSubType.name
|
||||
|
||||
|
||||
override fun createTargetIrs(module: Module): List<BuildSystemIR> = buildList {
|
||||
+DefaultTargetConfigurationIR(
|
||||
module.createTargetAccessIr(moduleSubType),
|
||||
createInnerTargetIrs(module)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Module.createTargetAccessIr(moduleSubType: ModuleSubType) =
|
||||
TargetAccessIR(
|
||||
moduleSubType,
|
||||
name.takeIf { it != moduleSubType.name }
|
||||
)
|
||||
|
||||
|
||||
interface JsTargetConfigurator : TargetConfigurator {
|
||||
override val moduleType: ModuleType get() = ModuleType.js
|
||||
}
|
||||
|
||||
object JsBrowserTargetConfigurator : JsTargetConfigurator {
|
||||
override val id = "jsBrowser"
|
||||
override val text = "Browser"
|
||||
|
||||
override fun createTargetIrs(module: Module): List<BuildSystemIR> = buildList {
|
||||
+DefaultTargetConfigurationIR(
|
||||
module.createTargetAccessIr(ModuleSubType.js),
|
||||
buildList {
|
||||
+RawGradleIR {
|
||||
sectionCall("browser") {}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object JsNodeTargetConfigurator : JsTargetConfigurator {
|
||||
override val id = "jsNode"
|
||||
override val text = "Node.js"
|
||||
|
||||
override fun createTargetIrs(module: Module): List<BuildSystemIR> = buildList {
|
||||
+DefaultTargetConfigurationIR(
|
||||
module.createTargetAccessIr(ModuleSubType.js),
|
||||
buildList {
|
||||
+RawGradleIR {
|
||||
sectionCall("nodejs") {}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object CommonTargetConfigurator : SimpleTargetConfigurator, SingleCoexistenceTargetConfigurator {
|
||||
override val moduleSubType = ModuleSubType.common
|
||||
}
|
||||
|
||||
object JvmTargetConfigurator : TargetConfigurator,
|
||||
SimpleTargetConfigurator,
|
||||
JvmModuleConfigurator,
|
||||
SingleCoexistenceTargetConfigurator {
|
||||
override val moduleSubType = ModuleSubType.jvm
|
||||
}
|
||||
|
||||
object AndroidTargetConfigurator : TargetConfigurator,
|
||||
SimpleTargetConfigurator,
|
||||
AndroidModuleConfigurator,
|
||||
SingleCoexistenceTargetConfigurator {
|
||||
override val moduleSubType = ModuleSubType.android
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user