From c5318ed063ea78bdf726f56b7c24e06362c92050 Mon Sep 17 00:00:00 2001 From: Brady Aiello Date: Sun, 12 Dec 2021 17:42:30 -0800 Subject: [PATCH] [Issue-10] Support Single Targets and Native-Grouped Targets [Summary] Some dependencies are only applicable to 1 target, and some apply to all native targets (except WASM, which almost no one supports yet). It would be nice to add in some target-specific dependencies, especially when they relate closely to a shared dependency (Ktor and SQLDelight are examples). [Fix] Create a "Single Target Libraries" and a "Native Target Libraries" section. Single Target libraries are available as long as there is a target selected that supports it. Native Target Libraries are available as long as there is at least one native target it builds for (excluding WASM). [Testing] - `./gradlew check` - manual testing --- .../generator/files/ModuleBuildGradle.kt | 57 ++++++++++++++-- .../jetbrains/webwiz/models/GradlePlugins.kt | 8 +-- .../jetbrains/webwiz/models/KmpLibraries.kt | 31 ++++++--- .../webwiz/models/NativeTargetLibrary.kt | 16 +++++ .../jetbrains/webwiz/models/ProjectInfo.kt | 11 ++- .../webwiz/models/SingleTargetLibrary.kt | 53 +++++++++++++++ .../jetbrains/webwiz/models/SourceSetType.kt | 11 +++ .../org/jetbrains/webwiz/models/Targets.kt | 10 +++ .../webwiz/generator/ProjectBuilderTest.kt | 6 ++ .../org/jetbrains/webwiz/content/Chips.kt | 68 ++++++++++++++++++- .../jetbrains/webwiz/content/WizardSection.kt | 20 ++++++ 11 files changed, 267 insertions(+), 24 deletions(-) create mode 100644 src/commonMain/kotlin/org/jetbrains/webwiz/models/NativeTargetLibrary.kt create mode 100644 src/commonMain/kotlin/org/jetbrains/webwiz/models/SingleTargetLibrary.kt create mode 100644 src/commonMain/kotlin/org/jetbrains/webwiz/models/SourceSetType.kt diff --git a/src/commonMain/kotlin/org/jetbrains/webwiz/generator/files/ModuleBuildGradle.kt b/src/commonMain/kotlin/org/jetbrains/webwiz/generator/files/ModuleBuildGradle.kt index 00cc62f..9203800 100644 --- a/src/commonMain/kotlin/org/jetbrains/webwiz/generator/files/ModuleBuildGradle.kt +++ b/src/commonMain/kotlin/org/jetbrains/webwiz/generator/files/ModuleBuildGradle.kt @@ -6,6 +6,10 @@ import org.jetbrains.webwiz.generator.deleteNans import org.jetbrains.webwiz.models.GradlePlugin import org.jetbrains.webwiz.models.KmpLibrary import org.jetbrains.webwiz.models.ProjectInfo +import org.jetbrains.webwiz.models.SourceSetDelegate +import org.jetbrains.webwiz.models.SourceSetDelegate.CREATING +import org.jetbrains.webwiz.models.SourceSetDelegate.GETTING +import org.jetbrains.webwiz.models.SourceSetType.MAIN import org.jetbrains.webwiz.models.Target import org.jetbrains.webwiz.models.isNativeTargetPresent @@ -84,7 +88,9 @@ kotlin { """.trimMargin().deleteNans() private fun commonMainSourceSet(): String { - val deps = projectInfo.dependencies.map { "implementation(\"${it.dep}\")" } + val deps = projectInfo.dependencies.filter { + it.sourceSetType.sourceSetTypeName == MAIN.sourceSetTypeName + }.map { "implementation(\"${it.dep}\")" } return if (deps.isEmpty()) { " | val commonMain by getting" } else { @@ -96,6 +102,25 @@ kotlin { } } + private fun singleSourceSet( + target: Target, + compilation: String, + sourceSetDelegate: SourceSetDelegate + ): String { + val deps = projectInfo.singleTargetDependencies + .filter { it.target == target && it.sourceSetType.sourceSetTypeName == compilation } + .map { "implementation(\"${it.dep}\")" } + return if (deps.isEmpty()) { + "val ${target.targetName}$compilation by ${sourceSetDelegate.delegate}" + } else { + """val ${target.targetName}$compilation by ${sourceSetDelegate.delegate} { + | dependencies { + | ${deps.joinToString("\n| ")} + | } + | }""" + } + } + private fun commonTestSourceSet() = """| val commonTest by getting { | dependencies { @@ -107,9 +132,9 @@ kotlin { val intention = "\n| " return projectInfo.targets.joinToString(intention) { when (it) { - Target.ANDROID -> "val android$compilation by getting" - Target.JVM -> "val jvm$compilation by getting" - Target.JS -> "val js$compilation by getting" + Target.ANDROID -> singleSourceSet(Target.ANDROID, compilation, GETTING) + Target.JVM -> singleSourceSet(Target.JVM, compilation, GETTING) + Target.JS -> singleSourceSet(Target.JS, compilation, GETTING) Target.WASM -> "val wasm32$compilation by getting" Target.ANDROID_NATIVE -> "val androidNativeArm64$compilation by getting" Target.LINUX -> "val linuxX64$compilation by getting" @@ -132,7 +157,7 @@ kotlin { Target.ANDROID_NATIVE -> "val androidNative$compilation by creating" Target.LINUX -> "val linux$compilation by creating" Target.MACOS -> "val macos$compilation by creating" - Target.IOS -> "val ios$compilation by creating" + Target.IOS -> singleSourceSet(Target.IOS, compilation, CREATING) Target.TV_OS -> "val tvos$compilation by creating" Target.WATCH_OS -> "val watchos$compilation by creating" Target.WINDOWS -> "val windows$compilation by creating" @@ -158,7 +183,27 @@ kotlin { } } - private fun nativeSourceSets(compilation: String) = "val native$compilation by creating" + private fun nativeUmbrellaSourceSet( + compilation: String, + sourceSetDelegate: SourceSetDelegate + ): String { + val deps = projectInfo.nativeTargetLibraries + .filter { it.sourceSetType.sourceSetTypeName == compilation } + .map { "implementation(\"${it.dep}\")" } + return if (deps.isEmpty()) { + "val native$compilation by ${sourceSetDelegate.delegate}" + } else { + """val native$compilation by ${sourceSetDelegate.delegate} { + | dependencies { + | ${deps.joinToString("\n| ")} + | } + | }""" + } + } + + private fun nativeSourceSets(compilation: String): String { + return nativeUmbrellaSourceSet(compilation, CREATING) + } private fun nativeSourceSetsDependencies(compilation: String) = "native$compilation.dependsOn(common$compilation)" private fun generateAndroidPluginConfig(minSdk: String, compileSdk: String) = """ diff --git a/src/commonMain/kotlin/org/jetbrains/webwiz/models/GradlePlugins.kt b/src/commonMain/kotlin/org/jetbrains/webwiz/models/GradlePlugins.kt index 7637bc9..aa1eb9c 100644 --- a/src/commonMain/kotlin/org/jetbrains/webwiz/models/GradlePlugins.kt +++ b/src/commonMain/kotlin/org/jetbrains/webwiz/models/GradlePlugins.kt @@ -17,13 +17,7 @@ enum class GradlePlugin( ), SQL_DELIGHT( emptySet(), - setOf( - Target.ANDROID_NATIVE, - Target.LINUX, - Target.WASM, - Target.TV_OS, - Target.WATCH_OS - ), + setOf(Target.WASM), "SQLDelight" ); diff --git a/src/commonMain/kotlin/org/jetbrains/webwiz/models/KmpLibraries.kt b/src/commonMain/kotlin/org/jetbrains/webwiz/models/KmpLibraries.kt index 1905a14..b9951d5 100644 --- a/src/commonMain/kotlin/org/jetbrains/webwiz/models/KmpLibraries.kt +++ b/src/commonMain/kotlin/org/jetbrains/webwiz/models/KmpLibraries.kt @@ -1,9 +1,12 @@ package org.jetbrains.webwiz.models +import org.jetbrains.webwiz.models.SourceSetType.MAIN + enum class KmpLibrary( val targets: Set?, //null means any target val userName: String, - val dep: String + val dep: String, + val sourceSetType: SourceSetType ) { COROUTINES( setOf( @@ -18,7 +21,8 @@ enum class KmpLibrary( Target.WINDOWS ), "KotlinX Coroutines", - "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2" + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2", + MAIN ), SERIALIZATION( setOf( @@ -33,7 +37,8 @@ enum class KmpLibrary( Target.WINDOWS ), "KotlinX Serialization", - "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1" + "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1", + MAIN ), DATE_TIME( setOf( @@ -48,7 +53,8 @@ enum class KmpLibrary( Target.WINDOWS ), "KotlinX DateTime", - "org.jetbrains.kotlinx:kotlinx-datetime:0.3.1" + "org.jetbrains.kotlinx:kotlinx-datetime:0.3.1", + MAIN ), KERMIT_LOGGER( setOf( @@ -63,7 +69,8 @@ enum class KmpLibrary( Target.WINDOWS ), "Kermit Logger", - "co.touchlab:kermit:1.0.0" + "co.touchlab:kermit:1.0.0", + MAIN ), NAPIER_LOGGER( setOf( @@ -76,7 +83,8 @@ enum class KmpLibrary( Target.WATCH_OS ), "Napier logger", - "io.github.aakira:napier:2.1.0" + "io.github.aakira:napier:2.1.0", + MAIN ), SQLDELIGHT_COROUTINES( setOf( @@ -84,11 +92,15 @@ enum class KmpLibrary( Target.JVM, Target.JS, Target.IOS, + Target.LINUX, Target.MACOS, - Target.WINDOWS + Target.WINDOWS, + Target.TV_OS, + Target.WATCH_OS ), "SQLDelight Coroutines", - "com.squareup.sqldelight:coroutines-extensions:1.5.3" + "com.squareup.sqldelight:coroutines-extensions:1.5.3", + MAIN ), KTOR_CORE( setOf( @@ -101,6 +113,7 @@ enum class KmpLibrary( Target.WINDOWS ), "Ktor Core", - "io.ktor:ktor-client-core:1.6.7" + "io.ktor:ktor-client-core:1.6.7", + MAIN ), } \ No newline at end of file diff --git a/src/commonMain/kotlin/org/jetbrains/webwiz/models/NativeTargetLibrary.kt b/src/commonMain/kotlin/org/jetbrains/webwiz/models/NativeTargetLibrary.kt new file mode 100644 index 0000000..9a3737a --- /dev/null +++ b/src/commonMain/kotlin/org/jetbrains/webwiz/models/NativeTargetLibrary.kt @@ -0,0 +1,16 @@ +package org.jetbrains.webwiz.models + +import org.jetbrains.webwiz.models.SourceSetType.MAIN + +// Dependencies in here will be available for all native targets +enum class NativeTargetLibrary( + val userName: String, + val dep: String, + val sourceSetType: SourceSetType +) { + SQLDELIGHT_DRIVER_NATIVE( + "SQDelight Native Driver", + "com.squareup.sqldelight:native-driver:1.5.3", + MAIN + ), +} \ No newline at end of file diff --git a/src/commonMain/kotlin/org/jetbrains/webwiz/models/ProjectInfo.kt b/src/commonMain/kotlin/org/jetbrains/webwiz/models/ProjectInfo.kt index 6b61512..1ffee8d 100644 --- a/src/commonMain/kotlin/org/jetbrains/webwiz/models/ProjectInfo.kt +++ b/src/commonMain/kotlin/org/jetbrains/webwiz/models/ProjectInfo.kt @@ -7,6 +7,8 @@ data class ProjectInfo( val kotlinVersion: KotlinVersion, val targets: Set, val dependencies: Set, + val singleTargetDependencies: Set, + val nativeTargetLibraries: Set, val gradlePlugins: Set, val enableTests: Boolean ) { @@ -20,11 +22,18 @@ data class ProjectInfo( require(dependencies.all { dep -> dep.targets == null || targets.all { dep.targets.contains(it) } }) { "incorrect dependency was used for current set of targets" } + require(singleTargetDependencies.all { dep -> dep.target in targets}) { + "incorrect dependency was used for current set of targets" + } } fun normalize() = copy( moduleName = moduleName.replace(' ', '_'), gradlePlugins = gradlePlugins.filter { it.canBeApplied(targets) }.toSet(), - dependencies = dependencies.filter { dep -> dep.targets == null || targets.all { dep.targets.contains(it) } }.toSet() + dependencies = dependencies + .filter { dep -> dep.targets == null || targets.all { dep.targets.contains(it) } }.toSet(), + singleTargetDependencies = singleTargetDependencies + .filter { dep -> dep.target in targets }.toSet(), + nativeTargetLibraries = if (this.targets.isNativeTargetPresent()) nativeTargetLibraries else emptySet() ) } diff --git a/src/commonMain/kotlin/org/jetbrains/webwiz/models/SingleTargetLibrary.kt b/src/commonMain/kotlin/org/jetbrains/webwiz/models/SingleTargetLibrary.kt new file mode 100644 index 0000000..39537f3 --- /dev/null +++ b/src/commonMain/kotlin/org/jetbrains/webwiz/models/SingleTargetLibrary.kt @@ -0,0 +1,53 @@ +package org.jetbrains.webwiz.models + +import org.jetbrains.webwiz.models.SourceSetType.MAIN + +enum class SingleTargetLibrary( + val target: Target, + val userName: String, + val dep: String, + val sourceSetType: SourceSetType +) { + KTOR_CLIENT_IOS( + Target.IOS, + "Ktor iOS Client", + "io.ktor:ktor-client-ios:1.6.7", + MAIN + ), + KTOR_CLIENT_OKHTTP( + Target.ANDROID, + "Ktor OkHttp Client", + "io.ktor:ktor-client-okhttp:1.6.7", + MAIN + ), + KTOR_CLIENT_JVM( + Target.JVM, + "Ktor JVM Client", + "io.ktor:ktor-client-jvm:1.6.7", + MAIN + ), + KTOR_CLIENT_JS( + Target.JS, + "Ktor JS Client", + "io.ktor:ktor-client-js:1.6.7", + MAIN + ), + SQLDELIGHT_DRIVER_ANDROID( + Target.ANDROID, + "SQLDelight Android Driver", + "com.squareup.sqldelight:android-driver:1.5.3", + MAIN + ), + SQLDELIGHT_DRIVER_JVM( + Target.JVM, + "SQLDelight JVM Driver", + "com.squareup.sqldelight:sqlite-driver:1.5.3", + MAIN + ), + SQLDELIGHT_DRIVER_JS( + Target.JS, + "SQDelight JS Driver", + "com.squareup.sqldelight:sqljs-driver:1.5.3", + MAIN + ), +} diff --git a/src/commonMain/kotlin/org/jetbrains/webwiz/models/SourceSetType.kt b/src/commonMain/kotlin/org/jetbrains/webwiz/models/SourceSetType.kt new file mode 100644 index 0000000..1f02155 --- /dev/null +++ b/src/commonMain/kotlin/org/jetbrains/webwiz/models/SourceSetType.kt @@ -0,0 +1,11 @@ +package org.jetbrains.webwiz.models + +enum class SourceSetType(val sourceSetTypeName: String) { + MAIN("Main"), + TEST("Test") +} + +enum class SourceSetDelegate(val delegate: String) { + CREATING("creating"), + GETTING("getting") +} \ No newline at end of file diff --git a/src/commonMain/kotlin/org/jetbrains/webwiz/models/Targets.kt b/src/commonMain/kotlin/org/jetbrains/webwiz/models/Targets.kt index c0a719e..249183b 100644 --- a/src/commonMain/kotlin/org/jetbrains/webwiz/models/Targets.kt +++ b/src/commonMain/kotlin/org/jetbrains/webwiz/models/Targets.kt @@ -25,8 +25,18 @@ private val NativeTargets = setOf( Target.WINDOWS ) +private val CommonNativeTargets = setOf( + Target.LINUX, + Target.MACOS, + Target.IOS, + Target.TV_OS, + Target.WATCH_OS, + Target.WINDOWS +) + fun Target.isJvm() = this in setOf(Target.JVM, Target.ANDROID) fun Set.isNativeTargetPresent() = this.any { it in NativeTargets } +fun Set.isCommonNativeTargetPresent() = this.any { it in CommonNativeTargets } fun Set.isAndroidTargetPresent() = this.any { it == Target.ANDROID } fun Set.isJvmTargetPresent() = this.any { it.isJvm() } diff --git a/src/commonTest/kotlin/org/jetbrains/webwiz/generator/ProjectBuilderTest.kt b/src/commonTest/kotlin/org/jetbrains/webwiz/generator/ProjectBuilderTest.kt index 0f70c73..e7d400e 100644 --- a/src/commonTest/kotlin/org/jetbrains/webwiz/generator/ProjectBuilderTest.kt +++ b/src/commonTest/kotlin/org/jetbrains/webwiz/generator/ProjectBuilderTest.kt @@ -22,6 +22,8 @@ internal class ProjectBuilderTest { KotlinVersion.Stable, setOf(JVM, JS, IOS, ANDROID), emptySet(), + emptySet(), + emptySet(), setOf(GradlePlugin.PUBLISH), true ) @@ -62,6 +64,8 @@ internal class ProjectBuilderTest { KotlinVersion.EAP, setOf(JVM, JS, IOS, ANDROID), setOf(KmpLibrary.SERIALIZATION), + emptySet(), + emptySet(), setOf(GradlePlugin.PUBLISH), true ) @@ -164,6 +168,8 @@ internal class ProjectBuilderTest { KotlinVersion.EAP, setOf(JVM, JS, IOS, ANDROID), setOf(KmpLibrary.SERIALIZATION), + emptySet(), + emptySet(), setOf(GradlePlugin.PUBLISH), true ) diff --git a/src/jsMain/kotlin/org/jetbrains/webwiz/content/Chips.kt b/src/jsMain/kotlin/org/jetbrains/webwiz/content/Chips.kt index 022714e..6c27ffc 100644 --- a/src/jsMain/kotlin/org/jetbrains/webwiz/content/Chips.kt +++ b/src/jsMain/kotlin/org/jetbrains/webwiz/content/Chips.kt @@ -2,10 +2,18 @@ package org.jetbrains.webwiz.content import androidx.compose.runtime.Composable import org.jetbrains.compose.web.attributes.disabled -import org.jetbrains.compose.web.dom.* +import org.jetbrains.compose.web.dom.CheckboxInput +import org.jetbrains.compose.web.dom.Div +import org.jetbrains.compose.web.dom.Label +import org.jetbrains.compose.web.dom.Span +import org.jetbrains.compose.web.dom.Text import org.jetbrains.webwiz.models.GradlePlugin import org.jetbrains.webwiz.models.KmpLibrary +import org.jetbrains.webwiz.models.NativeTargetLibrary +import org.jetbrains.webwiz.models.SingleTargetLibrary import org.jetbrains.webwiz.models.Target +import org.jetbrains.webwiz.models.isCommonNativeTargetPresent +import org.jetbrains.webwiz.models.isNativeTargetPresent import org.jetbrains.webwiz.style.WtOffsets @Composable @@ -63,6 +71,64 @@ fun LibrariesChips() { } } +@Composable +fun SingleTargetLibraryChips() { + Div({ classes(WtOffsets.targetsCheckboxesListStyle) }) { + SingleTargetLibrary.values().forEach { t -> + + if (t.target !in projectInfoState.value.targets ) { + return@forEach DisabledChip(t.userName) + } + + return@forEach Span({ classes(WtOffsets.targetsCheckboxesStyle) }) { + CheckboxInput(projectInfoState.value.singleTargetDependencies.contains(t)) { + onChange { event -> + val current = projectInfoState.value.singleTargetDependencies.toMutableSet() + val new: Set = when { + event.value -> current.plus(t) + else -> current.minus(t) + } + projectInfoState.value = projectInfoState.value.copy(singleTargetDependencies = new) + } + id("checkbox_${t.name}") + } + Label(forId = "checkbox_${t.name}") { + Text(t.userName) + } + } + } + } +} + +@Composable +fun NativeTargetLibraryChips() { + Div({ classes(WtOffsets.targetsCheckboxesListStyle) }) { + NativeTargetLibrary.values().forEach { t -> + + if (!projectInfoState.value.targets.isCommonNativeTargetPresent() ) { + return@forEach DisabledChip(t.userName) + } + + return@forEach Span({ classes(WtOffsets.targetsCheckboxesStyle) }) { + CheckboxInput(projectInfoState.value.nativeTargetLibraries.contains(t)) { + onChange { event -> + val current = projectInfoState.value.nativeTargetLibraries.toMutableSet() + val new: Set = when { + event.value -> current.plus(t) + else -> current.minus(t) + } + projectInfoState.value = projectInfoState.value.copy(nativeTargetLibraries = new) + } + id("checkbox_${t.name}") + } + Label(forId = "checkbox_${t.name}") { + Text(t.userName) + } + } + } + } +} + @Composable fun PluginsChips() { Div({ classes(WtOffsets.targetsCheckboxesListStyle) }) { diff --git a/src/jsMain/kotlin/org/jetbrains/webwiz/content/WizardSection.kt b/src/jsMain/kotlin/org/jetbrains/webwiz/content/WizardSection.kt index eba1530..7c40336 100644 --- a/src/jsMain/kotlin/org/jetbrains/webwiz/content/WizardSection.kt +++ b/src/jsMain/kotlin/org/jetbrains/webwiz/content/WizardSection.kt @@ -31,6 +31,8 @@ private val defaultProject = ProjectInfo( targets = setOf(Target.ANDROID, Target.IOS), enableTests = false, dependencies = setOf(KmpLibrary.SERIALIZATION), + singleTargetDependencies = emptySet(), + nativeTargetLibraries = emptySet(), gradlePlugins = emptySet() ).normalize() @@ -125,6 +127,24 @@ fun WizardSection(callback: (projectInfo: ProjectInfo) -> Unit) = Section({ } } + Row { + Div({ classes(WtOffsets.rowTargetsItems) }) { + Span({ classes(WtOffsets.textInputLabelsStyle) }) { + Text("Single Target Libraries") + } + SingleTargetLibraryChips() + } + } + + Row { + Div({ classes(WtOffsets.rowTargetsItems) }) { + Span({ classes(WtOffsets.textInputLabelsStyle) }) { + Text("Native Target Libraries") + } + NativeTargetLibraryChips() + } + } + Row { Div({ classes(WtOffsets.rowTargetsItems) }) { Span({ classes(WtOffsets.textInputLabelsStyle) }) {