From b2e0c99846b60618a24a8099e09407c7c8d259c6 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 12 Mar 2025 11:30:54 +0800 Subject: [PATCH 1/9] Set mainClass from kotlin.jvm().mainRun --- .../gradle/plugins/shadow/KmpPluginTest.kt | 13 ++++++++--- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 23 ++++++++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt index fc728c446..9c5ba3889 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt @@ -1,7 +1,10 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat +import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.BeforeEach @@ -21,11 +24,14 @@ class KmpPluginTest : BasePluginTest() { @Test fun compatKmpJvmTarget() { - val mainClass = writeClass(sourceSet = "jvmMain", isJava = false) + val mainClassName = "my.MainKt" + val mainClassPath = writeClass(sourceSet = "jvmMain", isJava = false) projectScriptPath.appendText( """ kotlin { - jvm() + jvm().mainRun { + mainClass = '$mainClassName' + } sourceSets { commonMain { dependencies { @@ -46,9 +52,10 @@ class KmpPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - mainClass, + mainClassPath, *entriesInAB, ) + getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt index c0c443041..355fc6691 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt @@ -1,15 +1,20 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.registerShadowJarCommon +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import kotlin.collections.contains import org.gradle.api.Plugin import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension public abstract class ShadowKmpPlugin : Plugin { + private lateinit var kmpExtension: KotlinMultiplatformExtension override fun apply(project: Project) { with(project) { - val kmpExtension = extensions.getByType(KotlinMultiplatformExtension::class.java) + kmpExtension = extensions.getByType(KotlinMultiplatformExtension::class.java) val kotlinJvmMain = kmpExtension.jvm().compilations.named("main") registerShadowJarCommon { task -> task.from(kotlinJvmMain.map { it.output.allOutputs }) @@ -18,6 +23,22 @@ public abstract class ShadowKmpPlugin : Plugin { listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) }, ) + configureMainClass(task) + } + } + } + + private fun configureMainClass(task: ShadowJar) { + if (KotlinVersion.CURRENT < KotlinVersion(2, 10, 20)) return + + @OptIn(ExperimentalKotlinGradlePluginApi::class) + kmpExtension.jvm().mainRun { + task.inputs.property("mainClassName", mainClass) + task.doFirst { + // Inject the attribute if it is not already present. + if (!task.manifest.attributes.contains(mainClassAttributeKey)) { + task.manifest.attributes[mainClassAttributeKey] = mainClass.get() + } } } } From 74d4cc6f4cb7b75cdbdcd85f5bc9dc2718cb2698 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 12 Mar 2025 11:43:54 +0800 Subject: [PATCH 2/9] Update changelog --- docs/changes/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index fe4105901..80347e7c2 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -7,6 +7,8 @@ - Add Kotlin DSL examples in docs. ([#1306](https://github.com/GradleUp/shadow/pull/1306)) - Support using type-safe dependency accessors in `ShadowJar.dependencies`. ([#1322](https://github.com/GradleUp/shadow/pull/1322)) +- Set `Main-Class` attr from `kotlin.jvm().mainRun.mainClass`. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) + This is added from Kotlin 2.1.20, which is still experimental. **Changed** From 2dd42260bab8a589f95a00c7556ddb62f318cd0f Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 12 Mar 2025 12:28:08 +0800 Subject: [PATCH 3/9] Update docs --- docs/changes/README.md | 2 +- docs/kmp-plugin/README.md | 10 ++++++++-- gradle/libs.versions.toml | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 80347e7c2..5ca415c29 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -8,7 +8,7 @@ - Add Kotlin DSL examples in docs. ([#1306](https://github.com/GradleUp/shadow/pull/1306)) - Support using type-safe dependency accessors in `ShadowJar.dependencies`. ([#1322](https://github.com/GradleUp/shadow/pull/1322)) - Set `Main-Class` attr from `kotlin.jvm().mainRun.mainClass`. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) - This is added from Kotlin 2.1.20, which is still experimental. + This is added from Kotlin 2.1.0, which is still experimental. **Changed** diff --git a/docs/kmp-plugin/README.md b/docs/kmp-plugin/README.md index 8c7c682e2..b9a3df6ba 100644 --- a/docs/kmp-plugin/README.md +++ b/docs/kmp-plugin/README.md @@ -15,7 +15,10 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target. val ktorVersion = "3.1.0" kotlin { - jvm() + jvm().mainRun { + // Optionally, set the main class for `runJvm`, it's avaliable from Kotlin 2.1.0 + mainClass = "myapp.MainKt" + } sourceSets { val commonMain by getting { dependencies { @@ -49,7 +52,10 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target. def ktorVersion = "3.1.0" kotlin { - jvm() + jvm().mainRun { + // Optionally, set the main class for `runJvm`, it's avaliable from Kotlin 2.1.0 + mainClass = "myapp.MainKt" + } sourceSets { commonMain { dependencies { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2f428546c..b018c9a5b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.1.20-RC2" +kotlin = "2.1.0" moshi = "1.15.2" [libraries] From 1478b5fb19b7b2f2095c4f4b9d869d010769574c Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 12 Mar 2025 13:01:44 +0800 Subject: [PATCH 4/9] Fix mainClass and version checker --- docs/kmp-plugin/README.md | 2 +- gradle/libs.versions.toml | 2 +- .../github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt | 6 +++--- .../jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/kmp-plugin/README.md b/docs/kmp-plugin/README.md index b9a3df6ba..a59dcb662 100644 --- a/docs/kmp-plugin/README.md +++ b/docs/kmp-plugin/README.md @@ -54,7 +54,7 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target. kotlin { jvm().mainRun { // Optionally, set the main class for `runJvm`, it's avaliable from Kotlin 2.1.0 - mainClass = "myapp.MainKt" + it.mainClass.set('myapp.MainKt') } sourceSets { commonMain { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b018c9a5b..2f428546c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.1.0" +kotlin = "2.1.20-RC2" moshi = "1.15.2" [libraries] diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt index 74a796113..92f28a9c1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt @@ -24,13 +24,13 @@ class KmpPluginTest : BasePluginTest() { @Test fun compatKmpJvmTarget() { - val mainClassEntry = "my.MainKt" - val mainClassPath = writeClass(sourceSet = "jvmMain", isJava = false) + val mainClassName = "my.MainKt" + val mainClassEntry = writeClass(sourceSet = "jvmMain", isJava = false) projectScriptPath.appendText( """ kotlin { jvm().mainRun { - mainClass = '$mainClassEntry' + it.mainClass.set('$mainClassName') } sourceSets { commonMain { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt index 355fc6691..40feb9715 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt @@ -8,6 +8,7 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion as KgpVersion public abstract class ShadowKmpPlugin : Plugin { private lateinit var kmpExtension: KotlinMultiplatformExtension @@ -29,7 +30,7 @@ public abstract class ShadowKmpPlugin : Plugin { } private fun configureMainClass(task: ShadowJar) { - if (KotlinVersion.CURRENT < KotlinVersion(2, 10, 20)) return + if (KgpVersion.DEFAULT < KgpVersion.KOTLIN_2_1) return @OptIn(ExperimentalKotlinGradlePluginApi::class) kmpExtension.jvm().mainRun { From a993fbc84c0ea7a2656c8d1c8841758f2f5acda7 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 12 Mar 2025 13:17:54 +0800 Subject: [PATCH 5/9] Wrap mainClass in provider block --- .../jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt index 40feb9715..5fb089448 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt @@ -29,16 +29,18 @@ public abstract class ShadowKmpPlugin : Plugin { } } - private fun configureMainClass(task: ShadowJar) { + private fun Project.configureMainClass(task: ShadowJar) { if (KgpVersion.DEFAULT < KgpVersion.KOTLIN_2_1) return @OptIn(ExperimentalKotlinGradlePluginApi::class) kmpExtension.jvm().mainRun { - task.inputs.property("mainClassName", mainClass) + // Fix cannot serialize object of type 'org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmRun'. + val mainClassName = provider { mainClass } + task.inputs.property("mainClassName", mainClassName) task.doFirst { // Inject the attribute if it is not already present. if (!task.manifest.attributes.contains(mainClassAttributeKey)) { - task.manifest.attributes[mainClassAttributeKey] = mainClass.get() + task.manifest.attributes[mainClassAttributeKey] = mainClassName.get().get() } } } From abb3f416000ff13514376496b77c3615e000901c Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 12 Mar 2025 13:27:51 +0800 Subject: [PATCH 6/9] Update changelog --- docs/changes/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 5ca415c29..702487abe 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -7,8 +7,7 @@ - Add Kotlin DSL examples in docs. ([#1306](https://github.com/GradleUp/shadow/pull/1306)) - Support using type-safe dependency accessors in `ShadowJar.dependencies`. ([#1322](https://github.com/GradleUp/shadow/pull/1322)) -- Set `Main-Class` attr from `kotlin.jvm().mainRun.mainClass`. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) - This is added from Kotlin 2.1.0, which is still experimental. +- Set `Main-Class` attr for KMP 2.1.0 or above. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) **Changed** From 8bfac6bb709fea6d32727c1525e162c86c0f67b9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 12 Mar 2025 13:33:44 +0800 Subject: [PATCH 7/9] Update docs/kmp-plugin/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/kmp-plugin/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/kmp-plugin/README.md b/docs/kmp-plugin/README.md index a59dcb662..6284c85fb 100644 --- a/docs/kmp-plugin/README.md +++ b/docs/kmp-plugin/README.md @@ -16,7 +16,7 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target. kotlin { jvm().mainRun { - // Optionally, set the main class for `runJvm`, it's avaliable from Kotlin 2.1.0 + // Optionally, set the main class for `runJvm`, it's available from Kotlin 2.1.0 mainClass = "myapp.MainKt" } sourceSets { From 89e85e5ef4e7e9e4b7fcc639bdcec79082087150 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 12 Mar 2025 13:33:51 +0800 Subject: [PATCH 8/9] Update docs/kmp-plugin/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/kmp-plugin/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/kmp-plugin/README.md b/docs/kmp-plugin/README.md index 6284c85fb..3bcdbc106 100644 --- a/docs/kmp-plugin/README.md +++ b/docs/kmp-plugin/README.md @@ -53,7 +53,7 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target. kotlin { jvm().mainRun { - // Optionally, set the main class for `runJvm`, it's avaliable from Kotlin 2.1.0 + // Optionally, set the main class for `runJvm`, it's available from Kotlin 2.1.0 it.mainClass.set('myapp.MainKt') } sourceSets { From 04ed3f538cc6ef0e3cc9b103167521314f4b2abe Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 12 Mar 2025 13:43:55 +0800 Subject: [PATCH 9/9] Add null check and test `canSetMainClassAttribute` --- .../gradle/plugins/shadow/KmpPluginTest.kt | 46 +++++++++++++++++-- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 6 +-- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt index 92f28a9c1..f00924258 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt @@ -9,6 +9,8 @@ import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class KmpPluginTest : BasePluginTest() { @BeforeEach @@ -24,14 +26,11 @@ class KmpPluginTest : BasePluginTest() { @Test fun compatKmpJvmTarget() { - val mainClassName = "my.MainKt" val mainClassEntry = writeClass(sourceSet = "jvmMain", isJava = false) projectScriptPath.appendText( """ kotlin { - jvm().mainRun { - it.mainClass.set('$mainClassName') - } + jvm() sourceSets { commonMain { dependencies { @@ -55,7 +54,44 @@ class KmpPluginTest : BasePluginTest() { mainClassEntry, *entriesInAB, ) - getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) + } + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun canSetMainClassAttribute(useShadowAttr: Boolean) { + val mainClassEntry = writeClass(sourceSet = "jvmMain", isJava = false) + val main2ClassEntry = writeClass(sourceSet = "jvmMain", isJava = false, className = "Main2") + val mainClassName = "my.Main" + val main2ClassName = "my.Main2" + val mainAttr = if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else "" + projectScriptPath.appendText( + """ + kotlin { + jvm().mainRun { + it.mainClass.set('$mainClassName') + } + } + $shadowJar { + manifest { + $mainAttr + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsEntries( + mainClassEntry, + main2ClassEntry, + ) + if (useShadowAttr) { + getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) + } else { + getMainAttr("Main-Class").isEqualTo(mainClassName) + } } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt index 5fb089448..b2204a1fe 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt @@ -38,9 +38,9 @@ public abstract class ShadowKmpPlugin : Plugin { val mainClassName = provider { mainClass } task.inputs.property("mainClassName", mainClassName) task.doFirst { - // Inject the attribute if it is not already present. - if (!task.manifest.attributes.contains(mainClassAttributeKey)) { - task.manifest.attributes[mainClassAttributeKey] = mainClassName.get().get() + val realClass = mainClassName.get().orNull + if (!task.manifest.attributes.contains(mainClassAttributeKey) && !realClass.isNullOrEmpty()) { + task.manifest.attributes[mainClassAttributeKey] = realClass } } }