Skip to content

Commit b5bcde9

Browse files
GooolerCopilot
andauthored
Set Main-Class attr for KMP 2.1.0 or above (#1337)
* Set mainClass from kotlin.jvm().mainRun * Update docs * Fix mainClass and version checker * Wrap mainClass in provider block for CC * Update changelog * Add null check and test `canSetMainClassAttribute` --------- Co-authored-by: Copilot <[email protected]>
1 parent 6457e1b commit b5bcde9

File tree

4 files changed

+77
-3
lines changed

4 files changed

+77
-3
lines changed

docs/changes/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
- Add Kotlin DSL examples in docs. ([#1306](https://github.com/GradleUp/shadow/pull/1306))
99
- Support using type-safe dependency accessors in `ShadowJar.dependencies`. ([#1322](https://github.com/GradleUp/shadow/pull/1322))
10+
- Set `Main-Class` attr for KMP 2.1.0 or above. ([#1337](https://github.com/GradleUp/shadow/pull/1337))
1011

1112
**Changed**
1213

docs/kmp-plugin/README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target.
1515
val ktorVersion = "3.1.0"
1616

1717
kotlin {
18-
jvm()
18+
jvm().mainRun {
19+
// Optionally, set the main class for `runJvm`, it's available from Kotlin 2.1.0
20+
mainClass = "myapp.MainKt"
21+
}
1922
sourceSets {
2023
val commonMain by getting {
2124
dependencies {
@@ -49,7 +52,10 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target.
4952
def ktorVersion = "3.1.0"
5053

5154
kotlin {
52-
jvm()
55+
jvm().mainRun {
56+
// Optionally, set the main class for `runJvm`, it's available from Kotlin 2.1.0
57+
it.mainClass.set('myapp.MainKt')
58+
}
5359
sourceSets {
5460
commonMain {
5561
dependencies {

src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package com.github.jengelman.gradle.plugins.shadow
22

33
import assertk.assertThat
4+
import assertk.assertions.isEqualTo
5+
import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey
46
import com.github.jengelman.gradle.plugins.shadow.util.containsEntries
7+
import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr
58
import kotlin.io.path.appendText
69
import kotlin.io.path.writeText
710
import org.junit.jupiter.api.BeforeEach
811
import org.junit.jupiter.api.Test
12+
import org.junit.jupiter.params.ParameterizedTest
13+
import org.junit.jupiter.params.provider.ValueSource
914

1015
class KmpPluginTest : BasePluginTest() {
1116
@BeforeEach
@@ -51,4 +56,42 @@ class KmpPluginTest : BasePluginTest() {
5156
)
5257
}
5358
}
59+
60+
@ParameterizedTest
61+
@ValueSource(booleans = [false, true])
62+
fun canSetMainClassAttribute(useShadowAttr: Boolean) {
63+
val mainClassEntry = writeClass(sourceSet = "jvmMain", isJava = false)
64+
val main2ClassEntry = writeClass(sourceSet = "jvmMain", isJava = false, className = "Main2")
65+
val mainClassName = "my.Main"
66+
val main2ClassName = "my.Main2"
67+
val mainAttr = if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else ""
68+
projectScriptPath.appendText(
69+
"""
70+
kotlin {
71+
jvm().mainRun {
72+
it.mainClass.set('$mainClassName')
73+
}
74+
}
75+
$shadowJar {
76+
manifest {
77+
$mainAttr
78+
}
79+
}
80+
""".trimIndent(),
81+
)
82+
83+
run(shadowJarTask)
84+
85+
assertThat(outputShadowJar).useAll {
86+
containsEntries(
87+
mainClassEntry,
88+
main2ClassEntry,
89+
)
90+
if (useShadowAttr) {
91+
getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName)
92+
} else {
93+
getMainAttr("Main-Class").isEqualTo(mainClassName)
94+
}
95+
}
96+
}
5497
}

src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package com.github.jengelman.gradle.plugins.shadow
22

33
import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.registerShadowJarCommon
4+
import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey
5+
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
6+
import kotlin.collections.contains
47
import org.gradle.api.Plugin
58
import org.gradle.api.Project
9+
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
610
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
11+
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion as KgpVersion
712

813
public abstract class ShadowKmpPlugin : Plugin<Project> {
14+
private lateinit var kmpExtension: KotlinMultiplatformExtension
915

1016
override fun apply(project: Project) {
1117
with(project) {
12-
val kmpExtension = extensions.getByType(KotlinMultiplatformExtension::class.java)
18+
kmpExtension = extensions.getByType(KotlinMultiplatformExtension::class.java)
1319
val kotlinJvmMain = kmpExtension.jvm().compilations.named("main")
1420
registerShadowJarCommon { task ->
1521
task.from(kotlinJvmMain.map { it.output.allOutputs })
@@ -18,6 +24,24 @@ public abstract class ShadowKmpPlugin : Plugin<Project> {
1824
listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName))
1925
},
2026
)
27+
configureMainClass(task)
28+
}
29+
}
30+
}
31+
32+
private fun Project.configureMainClass(task: ShadowJar) {
33+
if (KgpVersion.DEFAULT < KgpVersion.KOTLIN_2_1) return
34+
35+
@OptIn(ExperimentalKotlinGradlePluginApi::class)
36+
kmpExtension.jvm().mainRun {
37+
// Fix cannot serialize object of type 'org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmRun'.
38+
val mainClassName = provider { mainClass }
39+
task.inputs.property("mainClassName", mainClassName)
40+
task.doFirst {
41+
val realClass = mainClassName.get().orNull
42+
if (!task.manifest.attributes.contains(mainClassAttributeKey) && !realClass.isNullOrEmpty()) {
43+
task.manifest.attributes[mainClassAttributeKey] = realClass
44+
}
2145
}
2246
}
2347
}

0 commit comments

Comments
 (0)