From e76f49ae24f8f86f316ccb1adee3124f6ac92a24 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 09:24:53 +0800 Subject: [PATCH 01/16] Introduce KMP dependency --- build.gradle.kts | 2 ++ gradle/libs.versions.toml | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 81ba15916..24b04fc1c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,6 +51,7 @@ val testPluginClasspath by configurations.registering { } dependencies { + compileOnly(libs.kotlin.kmp) implementation(libs.apache.ant) implementation(libs.apache.commonsIo) implementation(libs.apache.log4j) @@ -62,6 +63,7 @@ dependencies { testPluginClasspath(libs.foojayResolver) testPluginClasspath(libs.pluginPublish) + testPluginClasspath(libs.kotlin.kmp) lintChecks(libs.androidx.gradlePluginLints) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7f847f636..07266742a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,4 +1,5 @@ [versions] +kotlin = "2.1.10" moshi = "1.15.2" [libraries] @@ -15,13 +16,14 @@ plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } -foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:0.9.0" pluginPublish = "com.gradle.publish:plugin-publish-plugin:1.3.1" mavenPublish = "com.vanniktech:gradle-maven-publish-plugin:0.30.0" gitPublish = "org.ajoberstar.git-publish:gradle-git-publish:5.1.0" jetbrains-dokka = "org.jetbrains.dokka:dokka-gradle-plugin:2.0.0" node = "com.github.node-gradle:gradle-node-plugin:7.1.0" +foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:0.9.0" +kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha03" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. @@ -31,7 +33,7 @@ junit-bom = "org.junit:junit-bom:5.12.0" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] -kotlin-jvm = "org.jetbrains.kotlin.jvm:2.1.10" +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } android-lint = "com.android.lint:8.8.1" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" spotless = "com.diffplug.spotless:7.0.2" From 70cfa6f152b543bcf64adb6ca3c71d2b56db71ce Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 09:28:56 +0800 Subject: [PATCH 02/16] Apply `ShadowJavaPlugin` for org.jetbrains.kotlin.multiplatform --- .../github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt index c7c7f908a..92483870c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt @@ -19,6 +19,10 @@ public abstract class ShadowPlugin : Plugin { withType(ApplicationPlugin::class.java) { apply(ShadowApplicationPlugin::class.java) } + withId("org.jetbrains.kotlin.multiplatform") { + apply(ShadowJavaPlugin::class.java) + } + // Apply the legacy plugin last. // Because we apply the ShadowJavaPlugin/ShadowApplication plugin in a withType callback for the // respective JavaPlugin/ApplicationPlugin, it may still apply before the shadowJar task is created etc. From 02409884a4fa78a2b311bf9a6e7d83de5183daeb Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 10:17:06 +0800 Subject: [PATCH 03/16] Configure `from` and `configurations` for org.jetbrains.kotlin.multiplatform --- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt index 4f147a3ea..9d140d387 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt @@ -24,6 +24,7 @@ import org.gradle.api.plugins.JavaPlugin import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension public abstract class ShadowJavaPlugin @Inject constructor( private val softwareComponentFactory: SoftwareComponentFactory, @@ -52,8 +53,6 @@ public abstract class ShadowJavaPlugin @Inject constructor( task.manifest.attributes[classPathAttributeKey] = attrs.joinToString(" ").trim() } } - task.from(sourceSets.named("main").map { it.output }) - task.configurations.convention(listOf(runtimeConfiguration)) task.exclude( "META-INF/INDEX.LIST", "META-INF/*.SF", @@ -63,6 +62,19 @@ public abstract class ShadowJavaPlugin @Inject constructor( "META-INF/versions/**/module-info.class", "module-info.class", ) + + if (plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")) { + val kmpExtension = extensions.getByType(KotlinMultiplatformExtension::class.java) + val kotlinJvmMain = kmpExtension.jvm().compilations.named("main") + + task.from(kotlinJvmMain.map { it.output.allOutputs }) + task.configurations.convention( + provider { listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) }, + ) + } else { + task.from(sourceSets.named("main").map { it.output }) + task.configurations.convention(provider { listOf(runtimeConfiguration) }) + } } artifacts.add(configurations.shadow.name, taskProvider) } From c7f8fef8f2cd74edb75de98374b276066a380237 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 15:01:28 +0800 Subject: [PATCH 04/16] Configure kmp related logic in `ShadowKmpPlugin` --- api/shadow.api | 6 +++++ .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 16 ++------------ .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 22 +++++++++++++++++++ .../gradle/plugins/shadow/ShadowPlugin.kt | 1 + 4 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt diff --git a/api/shadow.api b/api/shadow.api index 1feac44ae..3edca5a4d 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -58,6 +58,12 @@ public final class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$C public final fun getShadowRuntimeElements (Lorg/gradle/api/artifacts/ConfigurationContainer;)Lorg/gradle/api/NamedDomainObjectProvider; } +public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin : org/gradle/api/Plugin { + public fun ()V + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V +} + public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowPlugin : org/gradle/api/Plugin { public fun ()V public synthetic fun apply (Ljava/lang/Object;)V diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt index 9d140d387..43a8325c0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt @@ -24,7 +24,6 @@ import org.gradle.api.plugins.JavaPlugin import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension public abstract class ShadowJavaPlugin @Inject constructor( private val softwareComponentFactory: SoftwareComponentFactory, @@ -53,6 +52,8 @@ public abstract class ShadowJavaPlugin @Inject constructor( task.manifest.attributes[classPathAttributeKey] = attrs.joinToString(" ").trim() } } + task.from(sourceSets.named("main").map { it.output }) + task.configurations.convention(provider { listOf(runtimeConfiguration) }) task.exclude( "META-INF/INDEX.LIST", "META-INF/*.SF", @@ -62,19 +63,6 @@ public abstract class ShadowJavaPlugin @Inject constructor( "META-INF/versions/**/module-info.class", "module-info.class", ) - - if (plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")) { - val kmpExtension = extensions.getByType(KotlinMultiplatformExtension::class.java) - val kotlinJvmMain = kmpExtension.jvm().compilations.named("main") - - task.from(kotlinJvmMain.map { it.output.allOutputs }) - task.configurations.convention( - provider { listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) }, - ) - } else { - task.from(sourceSets.named("main").map { it.output }) - task.configurations.convention(provider { listOf(runtimeConfiguration) }) - } } artifacts.add(configurations.shadow.name, taskProvider) } 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 new file mode 100644 index 000000000..164417b43 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt @@ -0,0 +1,22 @@ +package com.github.jengelman.gradle.plugins.shadow + +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.shadowJar +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension + +public abstract class ShadowKmpPlugin : Plugin { + + override fun apply(project: Project) { + with(project) { + val kmpExtension = extensions.getByType(KotlinMultiplatformExtension::class.java) + val kotlinJvmMain = kmpExtension.jvm().compilations.named("main") + tasks.shadowJar.configure { task -> + task.from(kotlinJvmMain.map { it.output.allOutputs }) + task.configurations.convention( + provider { listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) }, + ) + } + } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt index 92483870c..310eeb85e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt @@ -21,6 +21,7 @@ public abstract class ShadowPlugin : Plugin { } withId("org.jetbrains.kotlin.multiplatform") { apply(ShadowJavaPlugin::class.java) + apply(ShadowKmpPlugin::class.java) } // Apply the legacy plugin last. From 32d9edf91d9c14760a534bae12bccabc77f15f6b Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 15:49:59 +0800 Subject: [PATCH 05/16] Test `compatKmpJvmTarget` --- .../gradle/plugins/shadow/KmpPluginTest.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt 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 new file mode 100644 index 000000000..65c802acf --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt @@ -0,0 +1,38 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import kotlin.io.path.appendText +import org.junit.jupiter.api.Test + +class KmpPluginTest : BasePluginTest() { + @Test + fun compatKmpJvmTarget() { + projectScriptPath.appendText( + """ + apply plugin: 'org.jetbrains.kotlin.multiplatform' + kotlin { + jvm() + sourceSets { + commonMain { + dependencies { + implementation 'my:b:1.0' + } + } + jvmMain { + dependencies { + implementation 'my:a:1.0' + } + } + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsEntries(*entriesInAB) + } + } +} From 3c716537d56778a08befe50f911c56c1dd64cdc9 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 15:50:45 +0800 Subject: [PATCH 06/16] Don't combine java plugin with kmp plugin --- .../gradle/plugins/shadow/BasePluginTest.kt | 4 ++-- .../gradle/plugins/shadow/KmpPluginTest.kt | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt index a2cadc5a4..a95758a93 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt @@ -103,7 +103,7 @@ abstract class BasePluginTest { val outputServerShadowJar: JarPath get() = jarPath("server/build/libs/server-1.0-all.jar") fun getDefaultProjectBuildScript( - javaPlugin: String = "java", + plugin: String = "java", withGroup: Boolean = false, withVersion: Boolean = false, ): String { @@ -111,7 +111,7 @@ abstract class BasePluginTest { val versionInfo = if (withVersion) "version = '1.0'" else "" return """ plugins { - id('$javaPlugin') + id('$plugin') id('com.gradleup.shadow') } $groupInfo 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 65c802acf..f74e29dab 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 @@ -3,14 +3,26 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import kotlin.io.path.appendText +import kotlin.io.path.writeText +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class KmpPluginTest : BasePluginTest() { + @BeforeEach + override fun setup() { + super.setup() + val projectBuildScript = getDefaultProjectBuildScript( + plugin = "org.jetbrains.kotlin.multiplatform", + withGroup = true, + withVersion = true, + ) + projectScriptPath.writeText(projectBuildScript) + } + @Test fun compatKmpJvmTarget() { projectScriptPath.appendText( """ - apply plugin: 'org.jetbrains.kotlin.multiplatform' kotlin { jvm() sourceSets { From 0ea49acb343d70709979878578d5d2adf33e0ea9 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 16:05:37 +0800 Subject: [PATCH 07/16] Extract `registerShadowJarCommon` --- api/shadow.api | 7 ++++ .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 34 ++++++++++++------- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 4 +-- .../gradle/plugins/shadow/ShadowPlugin.kt | 1 - 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 3edca5a4d..e9d073fb1 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -56,6 +56,13 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugi public final class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$Companion { public final fun getShadowJar (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; public final fun getShadowRuntimeElements (Lorg/gradle/api/artifacts/ConfigurationContainer;)Lorg/gradle/api/NamedDomainObjectProvider; + public final fun registerShadowJarCommon (Lorg/gradle/api/Project;Lkotlin/jvm/functions/Function1;)Lorg/gradle/api/tasks/TaskProvider; + public static synthetic fun registerShadowJarCommon$default (Lcom/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$Companion;Lorg/gradle/api/Project;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/gradle/api/tasks/TaskProvider; +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$sam$i$org_gradle_api_Action$0 : org/gradle/api/Action { + public fun (Lkotlin/jvm/functions/Function1;)V + public final synthetic fun execute (Ljava/lang/Object;)V } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin : org/gradle/api/Plugin { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt index 43a8325c0..91c99ae28 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt @@ -38,10 +38,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( protected open fun Project.configureShadowJar() { val jarTask = tasks.jar - val taskProvider = tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { task -> - task.group = ShadowBasePlugin.GROUP_NAME - task.description = "Create a combined JAR of project and runtime dependencies" - task.archiveClassifier.set("all") + val taskProvider = registerShadowJarCommon { task -> @Suppress("EagerGradleConfiguration") task.manifest.inheritFrom(jarTask.get().manifest) val attrProvider = jarTask.map { it.manifest.attributes[classPathAttributeKey]?.toString().orEmpty() } @@ -54,15 +51,6 @@ public abstract class ShadowJavaPlugin @Inject constructor( } task.from(sourceSets.named("main").map { it.output }) task.configurations.convention(provider { listOf(runtimeConfiguration) }) - task.exclude( - "META-INF/INDEX.LIST", - "META-INF/*.SF", - "META-INF/*.DSA", - "META-INF/*.RSA", - // module-info.class in Multi-Release folders. - "META-INF/versions/**/module-info.class", - "module-info.class", - ) } artifacts.add(configurations.shadow.name, taskProvider) } @@ -132,5 +120,25 @@ public abstract class ShadowJavaPlugin @Inject constructor( public inline val ConfigurationContainer.shadowRuntimeElements: NamedDomainObjectProvider get() = named(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) + + public inline fun Project.registerShadowJarCommon( + crossinline action: (ShadowJar) -> Unit = {}, + ): TaskProvider { + return tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { task -> + task.group = ShadowBasePlugin.GROUP_NAME + task.description = "Create a combined JAR of project and runtime dependencies" + task.archiveClassifier.set("all") + task.exclude( + "META-INF/INDEX.LIST", + "META-INF/*.SF", + "META-INF/*.DSA", + "META-INF/*.RSA", + // module-info.class in Multi-Release folders. + "META-INF/versions/**/module-info.class", + "module-info.class", + ) + action(task) + } + } } } 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 164417b43..0a3d6117e 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,6 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.shadowJar +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.registerShadowJarCommon import org.gradle.api.Plugin import org.gradle.api.Project import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension @@ -11,7 +11,7 @@ public abstract class ShadowKmpPlugin : Plugin { with(project) { val kmpExtension = extensions.getByType(KotlinMultiplatformExtension::class.java) val kotlinJvmMain = kmpExtension.jvm().compilations.named("main") - tasks.shadowJar.configure { task -> + registerShadowJarCommon { task -> task.from(kotlinJvmMain.map { it.output.allOutputs }) task.configurations.convention( provider { listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) }, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt index 310eeb85e..7a57fbd42 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt @@ -20,7 +20,6 @@ public abstract class ShadowPlugin : Plugin { apply(ShadowApplicationPlugin::class.java) } withId("org.jetbrains.kotlin.multiplatform") { - apply(ShadowJavaPlugin::class.java) apply(ShadowKmpPlugin::class.java) } From 2b92b10c8a2a11b49013bc6812948f04b9d11a6e Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 16:40:58 +0800 Subject: [PATCH 08/16] Check main class as well --- .../gradle/plugins/shadow/BasePluginTest.kt | 51 ++++++++++++------- .../gradle/plugins/shadow/KmpPluginTest.kt | 6 ++- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt index a95758a93..d814149fe 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt @@ -194,26 +194,43 @@ abstract class BasePluginTest { packageName: String = "my", withImports: Boolean = false, className: String = "Main", + isJava: Boolean = true, ): String { - val imports = if (withImports) "import junit.framework.Test;" else "" - val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" - path("src/$sourceSet/java/$packageName/$className.java").writeText( - """ - package $packageName; - $imports - public class $className { - public static void main(String[] args) { - if (args.length == 0) { - throw new IllegalArgumentException("No arguments provided."); + if (isJava) { + val imports = if (withImports) "import junit.framework.Test;" else "" + val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" + path("src/$sourceSet/java/$packageName/$className.java").writeText( + """ + package $packageName; + $imports + public class $className { + public static void main(String[] args) { + if (args.length == 0) throw new IllegalArgumentException("No arguments provided."); + String content = String.format("Hello, World! (%s) from $className", (Object[]) args); + System.out.println(content); + System.out.println($classRef); } - String content = String.format("Hello, World! (%s) from $className", (Object[]) args); - System.out.println(content); - System.out.println($classRef); } - } - """.trimIndent(), - ) - return packageName.replace('.', '/') + "/$className.class" + """.trimIndent(), + ) + } else { + val imports = if (withImports) "import junit.framework.Test;" else "" + val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" + path("src/$sourceSet/kotlin/$packageName/$className.kt").writeText( + """ + package $packageName + $imports + fun main(vararg args: String) { + if (args.isEmpty()) throw IllegalArgumentException("No arguments provided.") + val content ="Hello, World! (%s) from $className".format(*args) + println(content) + println($classRef) + } + """.trimIndent(), + ) + } + val baseClassPath = packageName.replace('.', '/') + "/$className" + return if (isJava) "$baseClassPath.class" else "${baseClassPath}Kt.class" } fun writeClientAndServerModules( 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 f74e29dab..0cd58c3ce 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 @@ -21,6 +21,7 @@ class KmpPluginTest : BasePluginTest() { @Test fun compatKmpJvmTarget() { + val mainClass = writeMainClass(sourceSet = "jvmMain", isJava = false) projectScriptPath.appendText( """ kotlin { @@ -44,7 +45,10 @@ class KmpPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries(*entriesInAB) + containsEntries( + mainClass, + *entriesInAB, + ) } } } From 48a84b44e60dc6ca578d41387717b6ad85c86ddb Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 16:55:20 +0800 Subject: [PATCH 09/16] Add `friends` configuration https://www.liutikas.net/2025/01/12/Kotlin-Library-Friends.html --- build.gradle.kts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 24b04fc1c..8dc253383 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,8 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.utils.extendsFrom plugins { alias(libs.plugins.kotlin.jvm) @@ -50,8 +52,16 @@ val testPluginClasspath by configurations.registering { isCanBeResolved = true } +// https://www.liutikas.net/2025/01/12/Kotlin-Library-Friends.html +val friends by configurations.registering { + isCanBeResolved = true + isCanBeConsumed = false + isTransitive = false +} +configurations.implementation.extendsFrom(friends) + dependencies { - compileOnly(libs.kotlin.kmp) + friends(libs.kotlin.kmp) implementation(libs.apache.ant) implementation(libs.apache.commonsIo) implementation(libs.apache.log4j) @@ -148,6 +158,10 @@ tasks.pluginUnderTestMetadata { ) } +tasks.withType().configureEach { + friendPaths.from(friends.map { it.incoming.artifactView { }.files }) +} + tasks.check { dependsOn(tasks.withType()) } From c08f88aa0298eb57c3e1fe788a3580884456aa72 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 17:12:41 +0800 Subject: [PATCH 10/16] Revert "Add `friends` configuration" This reverts commit 48a84b44e60dc6ca578d41387717b6ad85c86ddb. --- build.gradle.kts | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8dc253383..24b04fc1c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,8 +2,6 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.jetbrains.kotlin.gradle.utils.extendsFrom plugins { alias(libs.plugins.kotlin.jvm) @@ -52,16 +50,8 @@ val testPluginClasspath by configurations.registering { isCanBeResolved = true } -// https://www.liutikas.net/2025/01/12/Kotlin-Library-Friends.html -val friends by configurations.registering { - isCanBeResolved = true - isCanBeConsumed = false - isTransitive = false -} -configurations.implementation.extendsFrom(friends) - dependencies { - friends(libs.kotlin.kmp) + compileOnly(libs.kotlin.kmp) implementation(libs.apache.ant) implementation(libs.apache.commonsIo) implementation(libs.apache.log4j) @@ -158,10 +148,6 @@ tasks.pluginUnderTestMetadata { ) } -tasks.withType().configureEach { - friendPaths.from(friends.map { it.incoming.artifactView { }.files }) -} - tasks.check { dependsOn(tasks.withType()) } From 4f303afb597ec313cb451ddc1a660c8b6e13b590 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 17:17:08 +0800 Subject: [PATCH 11/16] Update changelog --- src/docs/changes/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index a29f644aa..feba84e73 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,11 @@ ## [Unreleased] +**Added** + +- Compat Kotlin Multiplatform plugin. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) + You still have to configure `manifest.attributes` (e.g. `Main-Class` attr) manually in the `shadowJar` task if necessary. + **Fixed** - Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) From ec506f7eb38ea2c9842cb414fd630841b18a9017 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 17:36:42 +0800 Subject: [PATCH 12/16] New doc for "Integrating with Kotlin Multiplatform Plugin" --- src/docs/.vuepress/config.js | 1 + src/docs/kmp-plugin/README.md | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/docs/kmp-plugin/README.md diff --git a/src/docs/.vuepress/config.js b/src/docs/.vuepress/config.js index 5e6b4b157..0d1794345 100644 --- a/src/docs/.vuepress/config.js +++ b/src/docs/.vuepress/config.js @@ -26,6 +26,7 @@ module.exports = { '/configuration/reproducible-builds/', '/custom-tasks/', '/application-plugin/', + '/kmp-plugin/', '/publishing/', '/multi-project/', '/plugins/', diff --git a/src/docs/kmp-plugin/README.md b/src/docs/kmp-plugin/README.md new file mode 100644 index 000000000..e9b668375 --- /dev/null +++ b/src/docs/kmp-plugin/README.md @@ -0,0 +1,38 @@ +# Integrating with Kotlin Multiplatform Plugin + +Shadow honors Kotlin's +[`org.jetbrains.kotlin.multiplatform`](https://kotlinlang.org/docs/multiplatform-intro.html) plugin and will automatically +configure additional tasks for bundling the shadowed JAR for it's `jvm` target. + +```groovy +// Using Shadow with KMP Plugin +plugins { + id 'org.jetbrains.kotlin.multiplatform' + id 'com.gradleup.shadow' +} + +def ktorVersion = "3.1.0" + +kotlin { + jvm() + sourceSets { + commonMain { + dependencies { + implementation "io.ktor:ktor-client-core$ktorVersion" + } + } + jvmMain { + dependencies { + implementation "io.ktor:ktor-client-okhttp$ktorVersion" + } + } + } +} + +tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + manifest { + // Optionally, set the main class for the shadowed JAR. + attributes 'Main-Class': 'com.example.MainKt' + } +} +``` From c51900b82435967cbb0a086562028748f3c9c50e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Feb 2025 17:39:42 +0800 Subject: [PATCH 13/16] Update src/docs/kmp-plugin/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/docs/kmp-plugin/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/docs/kmp-plugin/README.md b/src/docs/kmp-plugin/README.md index e9b668375..207c91e29 100644 --- a/src/docs/kmp-plugin/README.md +++ b/src/docs/kmp-plugin/README.md @@ -2,8 +2,7 @@ Shadow honors Kotlin's [`org.jetbrains.kotlin.multiplatform`](https://kotlinlang.org/docs/multiplatform-intro.html) plugin and will automatically -configure additional tasks for bundling the shadowed JAR for it's `jvm` target. - +configure additional tasks for bundling the shadowed JAR for its `jvm` target. ```groovy // Using Shadow with KMP Plugin plugins { From b09da447d870278fb6cbeaebc3e1e00d3a5eb11f Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 17:41:52 +0800 Subject: [PATCH 14/16] Refine --- src/docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index feba84e73..6b9c854f4 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -6,7 +6,7 @@ **Added** - Compat Kotlin Multiplatform plugin. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) - You still have to configure `manifest.attributes` (e.g. `Main-Class` attr) manually in the `shadowJar` task if necessary. + You still need to manually configure `manifest.attributes` (e.g. `Main-Class` attr) in the `shadowJar` task if necessary. **Fixed** From 8de94c3a415320b80a79cf1a03487274b85ff1ba Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 17:44:40 +0800 Subject: [PATCH 15/16] Tweak style --- .../github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 0a3d6117e..c0c443041 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 @@ -14,7 +14,9 @@ public abstract class ShadowKmpPlugin : Plugin { registerShadowJarCommon { task -> task.from(kotlinJvmMain.map { it.output.allOutputs }) task.configurations.convention( - provider { listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) }, + provider { + listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) + }, ) } } From a16f6092ce4f9c34cc7b0b8ce187a759a84a8a27 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 17:53:52 +0800 Subject: [PATCH 16/16] Improve `registerShadowJarCommon`'s interoperability for Groovy --- api/shadow.api | 9 ++------- .../jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt | 8 +++++--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index e9d073fb1..5570a339b 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -51,18 +51,13 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugi protected fun configureConfigurations (Lorg/gradle/api/Project;)V protected fun configureJavaGradlePlugin (Lorg/gradle/api/Project;)V protected fun configureShadowJar (Lorg/gradle/api/Project;)V + public static final fun registerShadowJarCommon (Lorg/gradle/api/Project;Lorg/gradle/api/Action;)Lorg/gradle/api/tasks/TaskProvider; } public final class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$Companion { public final fun getShadowJar (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; public final fun getShadowRuntimeElements (Lorg/gradle/api/artifacts/ConfigurationContainer;)Lorg/gradle/api/NamedDomainObjectProvider; - public final fun registerShadowJarCommon (Lorg/gradle/api/Project;Lkotlin/jvm/functions/Function1;)Lorg/gradle/api/tasks/TaskProvider; - public static synthetic fun registerShadowJarCommon$default (Lcom/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$Companion;Lorg/gradle/api/Project;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/gradle/api/tasks/TaskProvider; -} - -public final class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$sam$i$org_gradle_api_Action$0 : org/gradle/api/Action { - public fun (Lkotlin/jvm/functions/Function1;)V - public final synthetic fun execute (Ljava/lang/Object;)V + public final fun registerShadowJarCommon (Lorg/gradle/api/Project;Lorg/gradle/api/Action;)Lorg/gradle/api/tasks/TaskProvider; } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin : org/gradle/api/Plugin { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt index 91c99ae28..b93e64ee7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt @@ -8,6 +8,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration import com.github.jengelman.gradle.plugins.shadow.internal.sourceSets import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import javax.inject.Inject +import org.gradle.api.Action import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Plugin import org.gradle.api.Project @@ -121,8 +122,9 @@ public abstract class ShadowJavaPlugin @Inject constructor( public inline val ConfigurationContainer.shadowRuntimeElements: NamedDomainObjectProvider get() = named(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) - public inline fun Project.registerShadowJarCommon( - crossinline action: (ShadowJar) -> Unit = {}, + @JvmStatic + public fun Project.registerShadowJarCommon( + action: Action, ): TaskProvider { return tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { task -> task.group = ShadowBasePlugin.GROUP_NAME @@ -137,7 +139,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( "META-INF/versions/**/module-info.class", "module-info.class", ) - action(task) + action.execute(task) } } }