Skip to content

Add AndroidApplicationStorytaleGradlePlugin #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,26 +1,49 @@
package org.jetbrains.compose.storytale.plugin

import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget

open class StorytaleExtension(internal val project: Project) {
var buildDir: String = "storytale"

val multiplatformExtension = run {
val multiplatformClass =
tryGetClass<KotlinMultiplatformExtension>(
className = "org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension",
)
multiplatformClass?.let { project.extensions.findByType(it) } ?: error("UNEXPECTED")
}
val multiplatformProject: Boolean
val targets: List<KotlinTarget>
val mainStoriesSourceSet: KotlinSourceSet

init {
val multiplatformExtension = tryGetClass<KotlinMultiplatformExtension>(
className = "org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension",
)
?.let { project.extensions.findByType(it) }

val targets = multiplatformExtension.targets
val kotlinAndroidExtension = tryGetClass<KotlinAndroidExtension>(
className = "org.jetbrains.kotlin.gradle.dsl.KotlinAndroidExtension",
)
?.let { project.extensions.findByType(it) }

val mainStoriesSourceSet by lazy {
multiplatformExtension
.sourceSets
.create("common${StorytaleGradlePlugin.STORYTALE_SOURCESET_SUFFIX}")
val extension = checkNotNull(multiplatformExtension ?: kotlinAndroidExtension) {
"UNEXPECTED"
}
val multiplatformTargets = multiplatformExtension
?.targets
?.toList()
val androidTarget = kotlinAndroidExtension
?.target
?.let { listOf(it) }

multiplatformProject = multiplatformExtension != null
targets = multiplatformTargets ?: androidTarget ?: error("UNEXPECTED")
mainStoriesSourceSet = extension.sourceSets
.create(
if (multiplatformExtension != null) {
"common${StorytaleGradlePlugin.STORYTALE_SOURCESET_SUFFIX}"
} else {
StorytaleGradlePlugin.STORYTALE_SOURCESET_SUFFIX.lowercase()
},
)
.apply { setupCommonStoriesSourceSetDependencies(this) }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
class StorytaleGradlePlugin : KotlinCompilerPluginSupportPlugin {

override fun apply(project: Project) {
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eymar, Am I right that withId works lazily, so that the order of the plugins application doesn't matter if we use withId?

Copy link
Collaborator

@eymar eymar May 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

according to the documentation:

     * Executes or registers an action for a plugin with given id.
     * If the plugin was already applied, the action is executed.
     * If the plugin is applied sometime later the action will be executed after the plugin is applied.
     * If the plugin is never applied, the action is never executed.

so yes, the order of apply(plugin) calls doesn't matter when using withId.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the new implementation I see on the right side would require a strict order of plugins: Storytale would need to be registered after all other plugins - it's error prone. Ideally the order shouldn't matter.

Using nested withId is a solution which come to my mind. And btw I see it used to be here below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, yep, this is what I'm afraid of. @congvc-dev, could you rework it using the withId approach?

project.plugins.withId("org.jetbrains.compose") {
val extension =
project.extensions.create(STORYTALE_EXTENSION_NAME, StorytaleExtension::class.java, project)
project.processConfigurations(extension)
}
val multiplatformEnabled = project.plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")
val androidApplicationEnabled = project.plugins.hasPlugin("com.android.application")
val composeEnabled = project.plugins.hasPlugin("org.jetbrains.compose")
val composePluginEnabled = project.plugins.hasPlugin("org.jetbrains.kotlin.plugin.compose")
if (
(composeEnabled || composePluginEnabled) &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storytale strictly requires org.jetbrains.compose rather than org.jetbrains.kotlin.plugin.compose. So || is rather incorrect.

Did you want to make the new plugin to work on android without Compose Multiplatform?

(multiplatformEnabled || androidApplicationEnabled)
) {
val extension = project.extensions.create(
STORYTALE_EXTENSION_NAME,
StorytaleExtension::class.java,
project,
)
project.processConfigurations(extension)
}
}

Expand All @@ -32,17 +40,17 @@ class StorytaleGradlePlugin : KotlinCompilerPluginSupportPlugin {
override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>) = kotlinCompilation.target.project.provider { emptyList<SubpluginOption>() }

private fun Project.processConfigurations(extension: StorytaleExtension) {
extension.targets.all {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eymar Does all also work lazily?

Copy link
Collaborator

@eymar eymar May 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executes the given action against all objects in this collection, and any objects subsequently added to this collection.

So yes, all {} will be invoked on all targets added before and after.

forEach will just iterate on the current state of the collection.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same here. @congvc-dev could you use all instead of the forEach?

when (this) {
extension.targets.forEach {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was all replaced by forEach?

when (it) {
is KotlinJsIrTarget ->
when (wasmTargetType) {
KotlinWasmTargetType.JS -> processWasmCompilation(extension, this)
null -> processJsCompilation(extension, this)
when (it.wasmTargetType) {
KotlinWasmTargetType.JS -> processWasmCompilation(extension, it)
null -> processJsCompilation(extension, it)
else -> {}
}
is KotlinAndroidTarget -> processAndroidCompilation(extension, this)
is KotlinJvmTarget -> processJvmCompilation(extension, this)
is KotlinNativeTarget -> processNativeCompilation(extension, this)
is KotlinAndroidTarget -> processAndroidCompilation(extension, it)
is KotlinJvmTarget -> processJvmCompilation(extension, it)
is KotlinNativeTarget -> processNativeCompilation(extension, it)
}
}
}
Expand Down
Loading